Prd/components/dialer-patch/overlay/java/com/android/dialer/helpdesk/CallIdRegistry.java
Lucy Doupalů be9f14ce34 Helpdesk - operator console + patched GrapheneOS Dialer for call handling
A small helpdesk system: an office Pixel running a patched GrapheneOS Dialer
answers technician calls, records both call legs as separate channels, and a
Ruby backend transcribes them through Whisper and files an AI summary against
the caller.

Squashed to a single commit for sharing. No credentials are included; secrets
live outside the repo in /etc/helpdesk/env on the server or a gitignored
.claude/env.local locally. See .claude/env.local.example for the shape.

Start at README.md, then docs/architecture.md.
2026-07-27 18:50:32 +02:00

65 lines
2.4 KiB
Java

/*
* Helpdesk Dialer patch — per-call correlation registry.
* Part of the com.android.dialer.helpdesk overlay (see components/dialer-patch).
*/
package com.android.dialer.helpdesk;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.UUID;
/**
* Maps a live {@code DialerCall} (by its Dialer id) to our correlation {@code call_id} (UUIDv4),
* minted the first time the call is seen (at RINGING), plus per-call flags. The {@code call_id} is
* the single key that stitches the webhooks, the recording upload, the transcript and the reverse
* commands together. Thread-safe (touched from the Telecom/InCall callback thread).
*/
public final class CallIdRegistry {
public static final class Entry {
public final String callId = UUID.randomUUID().toString();
// Helpdesk: the call's start edge was announced (/calls/incoming for inbound, /calls/outgoing for
// outbound). Prevents re-firing the once-only start webhook on every onCallListChange tick.
public volatile boolean startReported;
public volatile boolean answeredReported;
public volatile String recordingUuid;
}
private final ConcurrentHashMap<String, Entry> byDialerId = new ConcurrentHashMap<>();
// Process-wide singleton. Shared by the in-call CallEventEmitter/RecordingBridge AND the always-on
// HelpdeskCommandService poller, so a reverse command that arrives between calls (e.g. `dial`) and one
// that arrives during a call (answer/mute/hold) both resolve against the same call_id <-> DialerCall map.
private static final CallIdRegistry INSTANCE = new CallIdRegistry();
public static CallIdRegistry get() {
return INSTANCE;
}
/** Get-or-create the entry, minting the call_id on first sight. */
public Entry forCall(String dialerId) {
return byDialerId.computeIfAbsent(dialerId, k -> new Entry());
}
public Entry peek(String dialerId) {
return byDialerId.get(dialerId);
}
public void remove(String dialerId) {
byDialerId.remove(dialerId);
}
/** Reverse lookup: the DialerCall id for our correlation call_id (used to apply reverse commands). */
public String dialerIdForCallId(String callId) {
if (callId == null) {
return null;
}
for (Map.Entry<String, Entry> e : byDialerId.entrySet()) {
if (callId.equals(e.getValue().callId)) {
return e.getKey();
}
}
return null;
}
}