fix: reduce event expiration to 20s and fix foreground service crash
- Change EXPIRATION_SECONDS from 300 to 20 for all call signaling events (offer, answer, ICE, hangup, reject, renegotiate) - Change MAX_EVENT_AGE_SECONDS from 30 to 20 to match - Update NIP-AC doc accordingly - Fix SecurityException crash on SDK 36: phoneCall foreground service type requires MANAGE_OWN_CALLS permission. Switch to microphone type which only needs RECORD_AUDIO (already granted). https://claude.ai/code/session_017hZm7yu7CzmcQgZGSaqSXS
This commit is contained in:
@@ -39,7 +39,7 @@
|
|||||||
<!-- Audio/Video Playback -->
|
<!-- Audio/Video Playback -->
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
|
||||||
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
|
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
|
||||||
|
|
||||||
<!-- Keeps screen on while playing videos -->
|
<!-- Keeps screen on while playing videos -->
|
||||||
@@ -225,7 +225,7 @@
|
|||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".service.call.CallForegroundService"
|
android:name=".service.call.CallForegroundService"
|
||||||
android:foregroundServiceType="phoneCall"
|
android:foregroundServiceType="microphone"
|
||||||
android:stopWithTask="true"
|
android:stopWithTask="true"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -62,7 +62,7 @@ class CallForegroundService : Service() {
|
|||||||
NOTIFICATION_ID,
|
NOTIFICATION_ID,
|
||||||
notification,
|
notification,
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL
|
ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -61,7 +61,7 @@ class CallManager(
|
|||||||
companion object {
|
companion object {
|
||||||
const val CALL_TIMEOUT_MS = 60_000L // 60 seconds ringing timeout
|
const val CALL_TIMEOUT_MS = 60_000L // 60 seconds ringing timeout
|
||||||
const val ENDED_DISPLAY_MS = 2_000L // show "call ended" briefly before resetting
|
const val ENDED_DISPLAY_MS = 2_000L // show "call ended" briefly before resetting
|
||||||
const val MAX_EVENT_AGE_SECONDS = 30L // discard signaling events older than this
|
const val MAX_EVENT_AGE_SECONDS = 20L // discard signaling events older than this
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isEventTooOld(event: Event): Boolean = TimeUtils.now() - event.createdAt > MAX_EVENT_AGE_SECONDS
|
private fun isEventTooOld(event: Event): Boolean = TimeUtils.now() - event.createdAt > MAX_EVENT_AGE_SECONDS
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ This NIP does not mandate specific STUN or TURN servers. Clients SHOULD:
|
|||||||
## Implementation Notes
|
## Implementation Notes
|
||||||
|
|
||||||
- The `call-id` tag MUST be a UUID that is unique per call session. All signaling events for the same call share the same `call-id`.
|
- The `call-id` tag MUST be a UUID that is unique per call session. All signaling events for the same call share the same `call-id`.
|
||||||
- Events SHOULD have short expiration times (~5 minutes) since signaling data is ephemeral and has no long-term value.
|
- Events SHOULD have short expiration times (~20 seconds) since signaling data is ephemeral and has no long-term value.
|
||||||
- Clients SHOULD implement a ringing timeout (e.g., 60 seconds). If no answer is received, the call transitions to a "timed out" state.
|
- Clients SHOULD implement a ringing timeout (e.g., 60 seconds). If no answer is received, the call transitions to a "timed out" state.
|
||||||
- Clients SHOULD use a foreground service or equivalent mechanism to keep calls active when the app is backgrounded.
|
- Clients SHOULD use a foreground service or equivalent mechanism to keep calls active when the app is backgrounded.
|
||||||
- The WebRTC `PeerConnection` SHOULD use Unified Plan SDP semantics.
|
- The WebRTC `PeerConnection` SHOULD use Unified Plan SDP semantics.
|
||||||
|
|||||||
+1
-1
@@ -48,7 +48,7 @@ class CallAnswerEvent(
|
|||||||
companion object {
|
companion object {
|
||||||
const val KIND = 25051
|
const val KIND = 25051
|
||||||
const val ALT_DESCRIPTION = "WebRTC call answer"
|
const val ALT_DESCRIPTION = "WebRTC call answer"
|
||||||
const val EXPIRATION_SECONDS = 300L
|
const val EXPIRATION_SECONDS = 20L
|
||||||
|
|
||||||
fun build(
|
fun build(
|
||||||
sdpAnswer: String,
|
sdpAnswer: String,
|
||||||
|
|||||||
+1
-1
@@ -48,7 +48,7 @@ class CallHangupEvent(
|
|||||||
companion object {
|
companion object {
|
||||||
const val KIND = 25053
|
const val KIND = 25053
|
||||||
const val ALT_DESCRIPTION = "WebRTC call hangup"
|
const val ALT_DESCRIPTION = "WebRTC call hangup"
|
||||||
const val EXPIRATION_SECONDS = 300L
|
const val EXPIRATION_SECONDS = 20L
|
||||||
|
|
||||||
fun build(
|
fun build(
|
||||||
peerPubKey: HexKey,
|
peerPubKey: HexKey,
|
||||||
|
|||||||
+1
-1
@@ -48,7 +48,7 @@ class CallIceCandidateEvent(
|
|||||||
companion object {
|
companion object {
|
||||||
const val KIND = 25052
|
const val KIND = 25052
|
||||||
const val ALT_DESCRIPTION = "WebRTC ICE candidate"
|
const val ALT_DESCRIPTION = "WebRTC ICE candidate"
|
||||||
const val EXPIRATION_SECONDS = 300L
|
const val EXPIRATION_SECONDS = 20L
|
||||||
|
|
||||||
fun build(
|
fun build(
|
||||||
candidateJson: String,
|
candidateJson: String,
|
||||||
|
|||||||
+1
-1
@@ -53,7 +53,7 @@ class CallOfferEvent(
|
|||||||
companion object {
|
companion object {
|
||||||
const val KIND = 25050
|
const val KIND = 25050
|
||||||
const val ALT_DESCRIPTION = "WebRTC call offer"
|
const val ALT_DESCRIPTION = "WebRTC call offer"
|
||||||
const val EXPIRATION_SECONDS = 300L // 5 minutes
|
const val EXPIRATION_SECONDS = 20L
|
||||||
|
|
||||||
fun build(
|
fun build(
|
||||||
sdpOffer: String,
|
sdpOffer: String,
|
||||||
|
|||||||
+1
-1
@@ -48,7 +48,7 @@ class CallRejectEvent(
|
|||||||
companion object {
|
companion object {
|
||||||
const val KIND = 25054
|
const val KIND = 25054
|
||||||
const val ALT_DESCRIPTION = "WebRTC call rejection"
|
const val ALT_DESCRIPTION = "WebRTC call rejection"
|
||||||
const val EXPIRATION_SECONDS = 300L
|
const val EXPIRATION_SECONDS = 20L
|
||||||
|
|
||||||
fun build(
|
fun build(
|
||||||
callerPubKey: HexKey,
|
callerPubKey: HexKey,
|
||||||
|
|||||||
+1
-1
@@ -48,7 +48,7 @@ class CallRenegotiateEvent(
|
|||||||
companion object {
|
companion object {
|
||||||
const val KIND = 25055
|
const val KIND = 25055
|
||||||
const val ALT_DESCRIPTION = "WebRTC call renegotiation"
|
const val ALT_DESCRIPTION = "WebRTC call renegotiation"
|
||||||
const val EXPIRATION_SECONDS = 300L
|
const val EXPIRATION_SECONDS = 20L
|
||||||
|
|
||||||
fun build(
|
fun build(
|
||||||
sdpOffer: String,
|
sdpOffer: String,
|
||||||
|
|||||||
Reference in New Issue
Block a user