Class Calls

java.lang.Object
com.aurelienpierre.sioul.Calls

final class Calls extends Object
Calls screened (docs/android.md, "Calls"; docs/research/call-screening.md, section 8). Android asks Sioul about each call (CallScreen, as the "Caller ID & spam app"); the answer comes from a table Sioul's Rust side wrote ahead (crates/sioul-app/src/calls.rs, `calls/table.json` in its state folder), read here in milliseconds, without Sioul's library or Qt. A call is let ring, or refused plainly: the network sends it to the operator's voicemail, as a call declined by hand; it stays in the phone's own call history. Every incoming call screened, declined or let ring, is written in Sioul's list (`held.jsonl`, `held`), which Rust reads and copies into this phone's log, for the Porch of each of your devices (docs/porch.md, "Calls declined"). The order, a table of version 2 (research 8.3; the call screening apart, crates/sioul-core/src/screening.rs): the floors first, which always ring (an emergency number or the emergency services' callback number; any call for a day after you call an emergency number; the numbers of `floors.people`, from an older table only); then your blocked numbers, refused; then, for a number nobody knows, what the screening keeps out whatever else (a number never shown as a caller's, a failed verification, an automated caller); then a second call from the same number within 15 minutes, which rings; then the people you know (your lists, your address books, this phone's contacts, a number you called in the last days, Always through), as the table's frame for now says: the times, Free time, and the layers above them (a slot of time for you, do-not-disturb from its switch or a focus session), a number you called beyond its row only while you expect a call; then everyone else by the call screening: Expecting a call, then what it lets ring by itself (off, resting in your work and admin hours, its rule for numbers nobody knows and hidden numbers), never where your contacts' row declines (review of part 2, 19 October 2026). A table of version 1 (an older Sioul's) is read in its own order: "Let every call through" before a second call, then the frame's row for everyone. Nothing to decide from (no table, a table past its frames, a row a frame does not name, anything that fails): it rings, as Android would. The JVM checks this order against Rust's (crates/sioul-core/src/calls.rs, `decide`) on tables as Rust writes them (android/jvm-checks/: DecideCheck, TableCheck). Here too: Rust's questions (StepService.call(android.content.Context, java.lang.String, java.lang.String), verbs "calls-…"); the press of Expecting a call (once "Let every call through") on the background service's notification, kept in a file of this phone's (`through-here.json`) that Rust reads and shares with your other devices; and the numbers you called lately, by their key, a fortnight at most and only while the call screening counts them, never shared (`called.json`), which count as your contacts.
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    (package private) static final class 
    What Sioul answers, and why: for the response, the list of calls held, and the log.
    (package private) static final class 
    One stretch of time and who rings in it.
    (package private) static interface 
    What the decision asks of the phone beyond the table: Android's emergency list, its contacts.
    (package private) static final class 
    A press of Expecting a call (once "Let every call through"): when (ms), on or off, until (ms; 0: until turned off).
    (package private) static final class 
    How the table's country writes its numbers (phones.rs's Region).
    (package private) static final class 
    The call screening, as a table of version 2 says it (crates/sioul-core/src/screening.rs, `TablePart`): what becomes of a number nobody knows, apart from do-not-disturb and every state.
    (package private) static final class 
    The table as Rust wrote it (crates/sioul-core/src/calls.rs, `Table`).
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    private static Calls.Table
     
    private static long
     
    private static long
     
    (package private) static final String
    The numbers you called lately, by their key, with when (this phone's own, never shared).
    private static final long
    A number called counts this long at most, whatever a table says (ms): screening.rs's CALLED_DAYS.
    private static final String
     
    private static final String
    Rust's state folder (android/main.cpp: XDG_STATE_HOME is files/state), and the calls' own in it.
    (package private) static final String
     
    private static final long
    Calls screened are kept this long in the list's file.
    private static final String
     
    (package private) static final String
     
    (package private) static final String
     
    (package private) static final String
     
    private static final long
    Past this size the list's file is rewritten without its old lines.
    private static final int
    The table's shape this code reads, and the one before it; another one is not read (the calls ring).
  • Constructor Summary

    Constructors
    Modifier
    Constructor
    Description
    private
     
  • Method Summary

    Modifier and Type
    Method
    Description
    (package private) static void
    append(File folder, String line, long now)
    A line added at the end of the list's file in `folder`; past its size, its lines older than a month dropped first.
    private static String
     
    private static Calls.Decision
    byFrame(Calls.Decision decision, Calls.Frame frame, String who)
    Who rings now, as the frame for now says; past the frames, nothing to decide from.
    private static Calls.Decision
    byFrame(Calls.Decision decision, Calls.Frame frame, String who, String own)
    As above, a person's own row (`own`, "own-0") read first when the frame has it: then "own", for the Porch's sentence (calls.rs's `decide`; review of part 6, F7).
    (package private) static String
    call(Context context, String verb, JSONObject asked)
    Rust's question (StepService.call(android.content.Context, java.lang.String, java.lang.String), verbs "calls-…"), from Sioul's own process or the service's.
    (package private) static long
    calledAt(File folder, String key, long now, long keepMs)
    When you last called this number from this phone (ms), within `keepMs` (14 days at most); 0 never, or forgotten.
    (package private) static long
    How long a number called counts, as the table says (0: not at all, the call screening counting none, or no table of version 2), never more than CALLED_MAX_MS.
    (package private) static Calls.Decision
    decide(Context context, Call.Details details, long now)
    Sioul's answer about a call (CallScreen), in milliseconds.
    (package private) static Calls.Decision
    decideFor(Calls.Table table, String raw, boolean hidden, long now, long emergencyAt, Calls.Press local, long lastCall, Calls.Phone phone)
    decideFor with no verification and no number called: as a table of version 1 is decided, and the checks before version 2.
    (package private) static Calls.Decision
    decideFor(Calls.Table table, String raw, boolean hidden, long now, long emergencyAt, Calls.Press local, long lastCall, Calls.Phone phone, String verified, long called)
    The decision itself, from what the phone knows (crates/sioul-core/src/ calls.rs, `decide`, takes the same steps, and its tests the same cases): the floors; who; blocked; then, version 2, what the screening keeps out of a number nobody knows whatever else, a second call, the people you know by the frame (a number called lately beyond its row only while you expect a call), everyone else by the call screening, never where your contacts' row declines but while you expect a call; version 1, Let every call through, a second call, the frame.
    private static boolean
     
    (package private) static long
    When you last called an emergency number (ms), as this phone saw it; 0 never.
    (package private) static File
    folder(Context context)
     
    private static boolean
     
    (package private) static void
    held(Context context, Calls.Decision decision)
    An incoming call screened, declined or let ring, in the list Rust reads and copies into this phone's log for your other devices (one JSON line each; lines older than a month dropped now and then).
    private static JSONObject
    keptCalls(JSONObject called, long now, long keep)
    The numbers called within `keep` before `now`, none ahead of it.
    (package private) static String
    key(String raw, Calls.Region region, Set<String> trunkZero)
    A number as the table keys it: phones.rs's `key` for what a caller ID holds (crates/sioul-core/src/calls.rs, `incoming_key`, does the same, and its tests check the two agree): "+33199001234"; a short code as written ("112", "3114"); a value that is not a number, as written without spaces.
    private static Calls.Press
    later(Calls.Press local, Calls.Press shared)
    The later of this phone's own press and your devices' latest, by when each was pressed.
    (package private) static String
    line(Calls.Decision decision, Calls.Table table)
    A call's line (crates/sioul-core/src/calls.rs, `Held`): when, its number's key and the number as the network gave it, hidden or not, who it was by your lists, the phone's contacts' name when the table did not know it, the time of day (the table's frame then, even for a call decided before it: a second call), why, whether it rang, the network's verification, whether it counted as your contacts because you called it lately.
    (package private) static String
    A number's key ("+33199001234") in the French plan's national form ("0199001234"), its overseas departments' included (+262, +590, +594, +596); null for any other (screening.rs, `national`).
    private static boolean
    Android's own blocked numbers (calls and texts, for every app): its page, for you to add one.
    private static boolean
    openRoles(Context context)
    Android's page of default apps, where "Caller ID & spam app" is chosen or set to none.
    (package private) static String
    phoneContact(Context context, String raw)
    The name this phone's contacts give a number; null when none has it (or contacts are not readable).
    (package private) static void
    press(Context context, boolean on, int minutes)
    Expecting a call (once "Let every call through") pressed on this phone (the notification): on for `minutes` (0: until turned off), or off.
    (package private) static String
    read(File file)
     
    private static JSONObject
    readJson(File file)
     
    private static long
    rememberCall(Context context, String key, long now)
    The number's last call before this one (ms, 0 none); this one remembered.
    (package private) static void
    rememberCalled(File folder, String key, long now, long keepMs)
    A number you called, by its key, with when (`called.json`, in `folder`, this phone's own, never shared), kept `keepMs` (14 days at most): the others pruned as it is written; with `keepMs` 0, nothing recorded and the file gone (review of part 2, F5).
    private static void
    rememberEmergency(Context context, long now)
     
    private static Calls.Decision
    said(Calls.Decision decision, boolean refuse, String why)
     
    private static boolean
    spacing(char c)
     
    private static String
    spam(Calls.Screening screening, String key, String verified)
    What the screening keeps out of a number nobody knows whatever else (decideFor's step 3; calls.rs's `spam`): a number never shown as a caller's, a failed verification, an automated caller, each while set to voicemail; null otherwise.
    private static boolean
    start(Context context, Intent intent)
     
    (package private) static JSONObject
    state(Context context)
    What this phone allows: {api, available (the role exists here), held (Sioul holds it), contacts (READ_CONTACTS), table (one is written), emergency_at (ms)}.
    (package private) static boolean
    An emergency number on Android's own list (the SIM's, the network's); false when it cannot say.
    (package private) static Calls.Table
    table(Context context)
    The table, read again only when its file changed; none when there is none or it does not read.
    private static void
    trim(File file, long now)
    The list's file without its lines older than a month.
    private static String
    trimmed(String value)
    Without the spaces and marks around it (phones.rs: `trim_matches` of both).
    private static String
    withoutWrittenTrunk(String digits, Set<String> trunkZero)
    "+330199…" → "+33199…": a trunk 0 written after a country code that has none.
    private static void
    writeWhole(File file, String text)
    Written beside, then renamed: never half a file.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

  • Constructor Details

  • Method Details

    • national

      static String national(String key)
      A number's key ("+33199001234") in the French plan's national form ("0199001234"), its overseas departments' included (+262, +590, +594, +596); null for any other (screening.rs, `national`).
    • digitsOnly

      private static boolean digitsOnly(String text)
    • folder

      static File folder(Context context)
    • table

      static Calls.Table table(Context context)
      The table, read again only when its file changed; none when there is none or it does not read.
    • key

      static String key(String raw, Calls.Region region, Set<String> trunkZero)
      A number as the table keys it: phones.rs's `key` for what a caller ID holds (crates/sioul-core/src/calls.rs, `incoming_key`, does the same, and its tests check the two agree): "+33199001234"; a short code as written ("112", "3114"); a value that is not a number, as written without spaces.
    • withoutWrittenTrunk

      private static String withoutWrittenTrunk(String digits, Set<String> trunkZero)
      "+330199…" → "+33199…": a trunk 0 written after a country code that has none.
    • spacing

      private static boolean spacing(char c)
    • trimmed

      private static String trimmed(String value)
      Without the spaces and marks around it (phones.rs: `trim_matches` of both).
    • asWritten

      private static String asWritten(String value)
    • systemEmergency

      static boolean systemEmergency(Context context, String raw)
      An emergency number on Android's own list (the SIM's, the network's); false when it cannot say.
    • phoneContact

      static String phoneContact(Context context, String raw)
      The name this phone's contacts give a number; null when none has it (or contacts are not readable).
    • decide

      static Calls.Decision decide(Context context, Call.Details details, long now)
      Sioul's answer about a call (CallScreen), in milliseconds.
    • decideFor

      static Calls.Decision decideFor(Calls.Table table, String raw, boolean hidden, long now, long emergencyAt, Calls.Press local, long lastCall, Calls.Phone phone)
      decideFor with no verification and no number called: as a table of version 1 is decided, and the checks before version 2.
    • decideFor

      static Calls.Decision decideFor(Calls.Table table, String raw, boolean hidden, long now, long emergencyAt, Calls.Press local, long lastCall, Calls.Phone phone, String verified, long called)
      The decision itself, from what the phone knows (crates/sioul-core/src/ calls.rs, `decide`, takes the same steps, and its tests the same cases): the floors; who; blocked; then, version 2, what the screening keeps out of a number nobody knows whatever else, a second call, the people you know by the frame (a number called lately beyond its row only while you expect a call), everyone else by the call screening, never where your contacts' row declines but while you expect a call; version 1, Let every call through, a second call, the frame. `verified`: the network's word ("passed", "failed", ""); `called`: when you last called this number (ms, 0 never).
    • spam

      private static String spam(Calls.Screening screening, String key, String verified)
      What the screening keeps out of a number nobody knows whatever else (decideFor's step 3; calls.rs's `spam`): a number never shown as a caller's, a failed verification, an automated caller, each while set to voicemail; null otherwise.
    • byFrame

      private static Calls.Decision byFrame(Calls.Decision decision, Calls.Frame frame, String who)
      Who rings now, as the frame for now says; past the frames, nothing to decide from.
    • byFrame

      private static Calls.Decision byFrame(Calls.Decision decision, Calls.Frame frame, String who, String own)
      As above, a person's own row (`own`, "own-0") read first when the frame has it: then "own", for the Porch's sentence (calls.rs's `decide`; review of part 6, F7).
    • later

      private static Calls.Press later(Calls.Press local, Calls.Press shared)
      The later of this phone's own press and your devices' latest, by when each was pressed.
    • said

      private static Calls.Decision said(Calls.Decision decision, boolean refuse, String why)
    • hasDigit

      private static boolean hasDigit(String text)
    • press

      static void press(Context context, boolean on, int minutes)
      Expecting a call (once "Let every call through") pressed on this phone (the notification): on for `minutes` (0: until turned off), or off. Kept for the screening at once; Rust shares it with your other devices at its next step.
    • rememberCall

      private static long rememberCall(Context context, String key, long now)
      The number's last call before this one (ms, 0 none); this one remembered. Calls older than an hour forgotten.
    • calledKeep

      static long calledKeep(Calls.Table table)
      How long a number called counts, as the table says (0: not at all, the call screening counting none, or no table of version 2), never more than CALLED_MAX_MS.
    • rememberCalled

      static void rememberCalled(File folder, String key, long now, long keepMs)
      A number you called, by its key, with when (`called.json`, in `folder`, this phone's own, never shared), kept `keepMs` (14 days at most): the others pruned as it is written; with `keepMs` 0, nothing recorded and the file gone (review of part 2, F5).
    • calledAt

      static long calledAt(File folder, String key, long now, long keepMs)
      When you last called this number from this phone (ms), within `keepMs` (14 days at most); 0 never, or forgotten. The file pruned as it is read, gone when nothing is left, when it does not read, or with `keepMs` 0.
    • keptCalls

      private static JSONObject keptCalls(JSONObject called, long now, long keep)
      The numbers called within `keep` before `now`, none ahead of it.
    • rememberEmergency

      private static void rememberEmergency(Context context, long now)
    • emergencyAt

      static long emergencyAt(Context context)
      When you last called an emergency number (ms), as this phone saw it; 0 never.
    • held

      static void held(Context context, Calls.Decision decision)
      An incoming call screened, declined or let ring, in the list Rust reads and copies into this phone's log for your other devices (one JSON line each; lines older than a month dropped now and then). Never an outgoing call; written after Android has its answer, so the ringing never waits.
    • line

      static String line(Calls.Decision decision, Calls.Table table)
      A call's line (crates/sioul-core/src/calls.rs, `Held`): when, its number's key and the number as the network gave it, hidden or not, who it was by your lists, the phone's contacts' name when the table did not know it, the time of day (the table's frame then, even for a call decided before it: a second call), why, whether it rang, the network's verification, whether it counted as your contacts because you called it lately. Null when it cannot be made.
    • append

      static void append(File folder, String line, long now)
      A line added at the end of the list's file in `folder`; past its size, its lines older than a month dropped first.
    • trim

      private static void trim(File file, long now)
      The list's file without its lines older than a month.
    • call

      static String call(Context context, String verb, JSONObject asked) throws JSONException
      Rust's question (StepService.call(android.content.Context, java.lang.String, java.lang.String), verbs "calls-…"), from Sioul's own process or the service's.
      Throws:
      JSONException
    • state

      static JSONObject state(Context context) throws JSONException
      What this phone allows: {api, available (the role exists here), held (Sioul holds it), contacts (READ_CONTACTS), table (one is written), emergency_at (ms)}.
      Throws:
      JSONException
    • openRoles

      private static boolean openRoles(Context context)
      Android's page of default apps, where "Caller ID & spam app" is chosen or set to none.
    • openBlocked

      private static boolean openBlocked(Context context)
      Android's own blocked numbers (calls and texts, for every app): its page, for you to add one.
    • start

      private static boolean start(Context context, Intent intent)
    • read

      static String read(File file) throws IOException
      Throws:
      IOException
    • readJson

      private static JSONObject readJson(File file)
    • writeWhole

      private static void writeWhole(File file, String text)
      Written beside, then renamed: never half a file.