/* * 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 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 e : byDialerId.entrySet()) { if (callId.equals(e.getValue().callId)) { return e.getKey(); } } return null; } }