BCR 是一款簡單的 Android 通話錄音應用程序,適用於已取得 root 權限的裝置或執行自訂韌體的裝置。一旦啟用,它就不會妨礙並在後台自動記錄來電和去電。
顧名思義,BCR 旨在盡可能成為基礎。如果該專案唯一需要的更新是與新的 Android 版本相容,那麼該專案就已經成功實現了其目標。因此,許多潛在有用的功能將永遠不會被實現,例如:
VOICE_CALL
音訊來源的裝置的解決方法(例如使用麥克風 + 揚聲器)從發布頁面下載最新版本。若要驗證數位簽名,請參閱驗證數位簽名部分。
將 BCR 安裝為系統應用程式。
對於使用 Magisk/KernelSU 植根的設備,只需從相應的應用程式中將 zip 刷新為 Magisk/KernelSU 模組即可。
.apk
並在重新啟動之前手動安裝。這是解決韌體中的錯誤所必需的,該錯誤導致未建立應用程式資料目錄,從而導致 BCR 打開空白螢幕。對於未 root 的自訂固件,請在啟動恢復時刷新 zip。
READ_CALL_LOG
權限在 Android 10+ 中受到嚴格限制,即使透過 Android 設定也無法授予該權限。若要消除此限制,請在重新引導回 Android 後透過 adb 運行: # If rooted, run inside of `su`:
CLASSPATH=/system/priv-app/com.chiller3.bcr/app-release.apk app_process / com.chiller3.bcr.standalone.RemoveHardRestrictionsKt
# If unrooted, install BCR as both a user app and a system app:
pm install /system/priv-app/com.chiller3.bcr/app-release.apk
system
分割區使用erofs
進行格式化,則檔案系統是唯讀的,並且無法使用此方法。644
權限和u:object_r:system_file:s0
SELinux 標籤,也可以從 zip 中的system/
資料夾手動提取檔案。重新啟動並開啟 BCR。
如果安裝了其他通話錄音器,請確保已停用其通話錄音功能。在大多數設備上,兩個應用程式無法同時錄製電話。但是,讓 BCR 記錄電話和其他應用程式記錄是可以的,例如。 VOIP 通話。
啟用通話錄音並選擇輸出目錄。
如果未選擇輸出目錄或輸出目錄不再可訪問,則錄音將儲存到/sdcard/Android/data/com.chiller3.bcr/files
。請注意,在 Android 12+ 上, /sdcard/Android/data/
只能透過 USB 或 DocumentsUI(AOSP 的內建檔案管理器)存取。
首次啟用通話錄音時,BCR 會提示輸入麥克風、通知(Android 13+)、通話記錄、聯絡人和電話權限。基本通話錄音功能僅需麥克風和通知權限。如果授予其他權限,則會將更多資訊新增至輸出檔名。例如,聯絡人權限將允許將聯絡人姓名新增至檔案名稱。
有關權限的更多詳細信息,請參閱下面的權限部分。
要安裝未來的更新,有幾種方法:
.apk
並直接安裝。使用此方法,舊版本作為系統應用程式存在,新版本作為使用者安裝的系統應用程式更新存在。如果將 BCR 烘焙到 Android 韌體映像中,此方法會更方便。 與某些裝置上預先安裝的撥號器應用程式內建的通話錄音功能不同,BCR 不會向對方宣布通話正在錄音。 BCR 絕不會向呼叫音訊串流輸出任何類型的音訊。
啟用 BCR 後,完全避免使用撥號器內建的通話記錄器。使用它很有可能會導致意外行為,例如兩次錄音都失敗或撥號器宣布呼叫正在錄音。
如果您居住在需要兩方同意的司法管轄區,您有責任通知另一方通話正在錄音。如果需要,可以使用自動錄製規則預設丟棄錄製內容。但請注意,如果您選擇在通話過程中保留錄音,則錄音將包含完整的通話內容,而不僅僅是對方同意後的部分。
BCR 具有直接啟動感知能力,這意味著它能夠在設備重新啟動後最初解鎖之前運行和錄製呼叫。在此狀態下,除了需要聯絡人清單或通話記錄的功能外,BCR 的大部分功能仍然有效。實際上,這意味著:
但是,如果裝置在通話結束前解鎖,則這些限制均不適用。
請注意,在設備首次解鎖之前,輸出目錄不可用。在該狀態下進行的錄音儲存在使用者無法存取的內部目錄中。裝置解鎖後,BCR 會將檔案移至輸出目錄。這可能需要一些時間才能完成。
CAPTURE_AUDIO_OUTPUT
(由系統應用程式權限自動授予)CONTROL_INCALL_EXPERIENCE
(由系統應用程式權限自動授予)RECORD_AUDIO
(必須由使用者授予)FOREGROUND_SERVICE
、 FOREGROUND_SERVICE_MICROPHONE
(安裝時自動授予)POST_NOTIFICATIONS
(必須由 Android 13+ 上的使用者授予)READ_CALL_LOG
(可選)READ_CONTACTS
(選購)RECEIVE_BOOT_COMPLETED
、 FOREGROUND_SERVICE_SPECIAL_USE
(安裝時自動授予)READ_PHONE_STATE
(可選)REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
(可選)VIBRATE
(在安裝時自動授予)請注意, INTERNET
不在清單中。 BCR 不會也永遠不會上網。 BCR 也永遠不會與其他應用程式通信,除非使用者明確點擊錄製完成時顯示的通知中的Open
或Share
按鈕。在這種情況下,目標應用程式僅被授予對該單一錄音的存取權限。
BCR 對呼叫重定向應用程式(例如 Google Voice)的支援有限。只有當呼叫重新導向服務在背景使用標準電話(而非 VOIP)時,才能錄製重新導向的通話。
與常規呼叫相比,錄製重定向呼叫時存在一些限制:
All other calls
規則。存在這些限制是因為當呼叫被重定向時,只有撥號器應用程式本身知道原始電話號碼。 Android 電話系統不知道這一點。當撥號器在通話結束時新增條目時,BCR 只能透過搜尋系統通話記錄找到原始電話號碼。
BCR 支援自訂用於確定錄音輸出檔案名稱的範本。預設模板是:
{date}[_{direction}|][_sim{sim_slot}|][_{phone_number}|][_[{contact_name}|{caller_name}|{call_log_name}]|]
{var}
) 用來引用變數。變數被它們所代表的值取代。例如, {phone_number}
會取代為通話的實際電話號碼。[{var}|default]
) 用於指定後備。例如,如果號碼在聯絡人中, [{contact_name}|{caller_name}|Unknown]
將插入聯絡人姓名。否則,如果聯絡人姓名和來電者 ID 都不存在,它將回退到呼叫者 ID 或Unknown
。回退到空字串也是完全有效的。例如, [{contact_name}|]
計算結果為聯絡人姓名或不計算任何內容。{date}
:呼叫的時間戳記。預設時間戳格式盡可能明確,格式為: 20230414_215701.088-0400
。可以使用{date:<format string>}
指定自訂時間戳格式。例如, {date:yyyy-MM-dd @ h.mm.ss a}
將產生2023-04-14 @ 9.57.01 PM
。時間戳格式字元的完整清單可以在以下位置找到:https://developer.android.com/reference/java/time/format/DateTimeFormatterBuilder#appendPattern(java.lang.String)。{phone_number}{date}
將導致檔案保留被停用,但{phone_number} ({date})
可以工作,因為兩個變數之間有一些文字(
。yyMMdd_HHmmss
變更為HHmmss_yyMMdd
,則舊記錄檔案名稱中的時間戳記將被錯誤解析並可能被刪除。{direction}
: [僅限 Android 10+]一對一通話,根據通話是來電還是out
,可以in
或撥出。如果呼叫是電話會議,則使用conference
。{sim_slot}
: [僅限 Android 11+]通話的 SIM 插槽編號(從 1 開始計數)。這僅針對具有多個活動 SIM 卡且 BCR 被授予「電話」權限的多 SIM 卡設備定義。{sim_slot:always}
。{phone_number}
:通話的電話號碼。對於私人呼叫,這是未定義的。可用的格式選項:{phone_number:E.164}
:預設(與{phone_number}
相同)。採用國際 E.164 格式的電話號碼 ( +<country code><subscriber>
)。{phone_number:digits_only}
:只包含數字的電話號碼(無+
或分隔符號)。{phone_number:formatted}
:使用國家/地區特定樣式格式化的電話號碼。{caller_name}
: CNAP 從業者提供的來電者 ID。{contact_name}
與電話號碼相關的(第一個)聯絡人的姓名。僅當 BCR 被授予聯絡人權限時才定義此值。{call_log_name}
:通話記錄中顯示的名稱。如果系統撥號程式執行反向查找,這可能包括更多信息,例如企業名稱。僅當 BCR 被授予讀取呼叫日誌權限時才定義此值。檔案名稱範本支援使用/
字元指定子目錄。檔案名稱範本中的任何位置都允許使用斜杠,包括{date}
(例如{date:yyyy/MM/dd}
)。但是,展開其他變數後出現的任何斜線都將替換為底線。例如,如果呼叫的主叫方 ID 為First/Last
,則{caller_name}
會擴充為First_Last
。
請注意,由於 Android 儲存存取框架的效能較差,使用子目錄可能會顯著減慢某些裝置上錄製內容的保存速度。在具有良好 SAF 實現的 Android 建置上,這可能只需幾秒鐘。在採用最糟糕的已知 SAF 實現的 OEM Android 建置上,這可能需要幾分鐘的時間。延遲與輸出目錄中的檔案數量成正比。
如果啟用Write metadata file
選項,BCR 會將 JSON 檔案寫入輸出目錄,其中包含 BCR 所了解的有關通話的所有詳細資訊以及有關錄製音訊的資訊。該檔案與音訊檔案同名,但副檔名為.json
。
JSON 結構如以下範例所示。請注意,僅保證timestamp_unix_ms
、 timestamp
和output.format.*
存在。如果無法確定欄位的值(例如,當發生錯誤或所需權限被拒絕時),則將其設為null
。
{
// The timestamp represented as milliseconds since the Unix epoch in UTC.
"timestamp_unix_ms" : 1689817988931 ,
// The timestamp represented as ISO8601 (+ offset) in the local time zone.
"timestamp" : "2023-07-19T21:53:08.931-04:00" ,
// The call direction ("in", "out", or "conference").
// [Android 10+ only]
"direction" : "in" ,
// The SIM slot used for the call.
// [Android 11+ only; requires the Phone permission]
"sim_slot" : 1 ,
// The name shown in the dialer's call log. This may include the business'
// name for dialers that perform reverse lookups.
// [Requires the Call Log permission]
"call_log_name" : "John Doe" ,
// Details about the other party or parties in the call. There will be
// multiple entries for conference calls.
"calls" : [
{
// The raw phone number as reported by Android. For outgoing calls,
// this is usually what the user typed. For incoming calls, this is
// usually E.164 formatted. This will be null for private calls.
"phone_number" : "+11234567890" ,
// The phone number formatted using the country-specific style. This
// will be null for private calls or if Android cannot determine the
// country.
"phone_number_formatted" : "+1 123-456-7890" ,
// The caller name/ID as reported by CNAP from the carrier.
"caller_name" : "John Doe" ,
// The contact name associated with the phone number.
// [Requires the Contacts permission]
"contact_name" : "John Doe"
}
] ,
// Details about the output file.
"output" : {
// Details about the output file format.
"format" : {
// The audio encoding format.
"type" : "OGG/Opus" ,
// The MIME type of the container format (eg. OGG).
"mime_type_container" : "audio/ogg" ,
// The MIME type of the raw audio stream (eg. Opus).
"mime_type_audio" : "audio/opus" ,
// The type of the parameter value below. Either "bitrate",
// "compression_level", or "none".
"parameter_type" : "bitrate" ,
// The encoder quality/size parameter.
"parameter" : 48000 ,
} ,
// Details about the recording and encoding process. If the recording
// process fails, this is set to null.
"recording" : {
// The total number of audio frames that BCR read from the audio
// device. This includes the periods of time when the recording was
// paused or on hold.
// (Number of frames == number of samples * channel count)
"frames_total" : 96000 ,
// The number of audio frames that were actually saved to the output
// file. This excludes the periods of time when the recording was
// paused or on hold.
// (Number of frames == number of samples * channel count)
"frames_encoded" : 48000 ,
// The number of samples per second of audio.
"sample_rate" : 48000 ,
// The number of channels in the audio. This is currently always 1
// because no device supports stereo call audio.
"channel_count" : 1 ,
// The total time in seconds that BCR read from the audio device.
// (Equal to: frames_total / sample_rate / channel_count)
"duration_secs_total" : 2.0 ,
// The time in seconds of audio actually saved to the output file.
// (Equal to: frames_encoded / sample_rate / channel_count)
"duration_secs_encoded" : 1.0 ,
// The size of the recording buffer in frames. This is the maximum
// number of audio frames read from the audio driver before it is
// passed to the audio encoder.
"buffer_frames" : 640 ,
// The number of buffer overruns. This is the number of times that
// the CPU or storage couldn't keep up while encoding the raw audio,
// resulting in skips (loss of audio).
"buffer_overruns" : 0 ,
// Whether the call was ever paused by the user.
"was_ever_paused" : false ,
// Whether the call was ever placed on hold (call waiting).
"was_ever_holding" : false
}
}
}
本節介紹 BCR 隱藏的進階功能。
BCR 有一個隱藏的偵錯模式,可以透過長按版本號來啟用或停用。
啟用偵錯模式時,BCR 會在通話錄音完成後將日誌檔案寫入輸出目錄。它的命名方式與音訊檔案相同。日誌檔案包含與adb logcat
將顯示的訊息相同的訊息,但與 BCR 無關的訊息被過濾掉(無論如何,BCR 無權存取這些訊息)。
在日誌檔案中,BCR 的目標是永遠不記錄任何敏感資訊。有關目前通話的資訊(例如電話號碼)將替換為佔位符,例如<phone number>
。然而,其他資訊不能輕易編輯,這樣就會被截斷。例如,當檔案保留功能清理舊檔案時,諸如20230101_010203.456+0000_out_1234567890_John_Doe.oga
之類的檔案名稱將記錄為20<...>ga
。
報告錯誤時,請包含日誌文件,因為它對於識別可能出現的錯誤非常有幫助。 (但請仔細檢查日誌檔案以確保沒有敏感資訊!)
BCR 嚴重依賴系統應用程式權限才能正常運作。這主要是因為兩個權限:
CONTROL_INCALL_EXPERIENCE
此權限允許 Android 的電話服務綁定到 BCR 的InCallService
,而無需 BCR 成為可穿戴配套應用程式、汽車 UI 或預設撥號器。綁定後,服務將接收呼叫變更事件的回呼(例如,振鈴狀態下的來電)。此方法比使用READ_PHONE_STATE
權限並依賴android.intent.action.PHONE_STATE
廣播可靠得多。
這種方法還有一些額外的好處。由於電話服務綁定到 BCR 的InCallService
的方式,該服務可以在通話過程中根據需要將自身帶入和帶出前台,並訪問音頻流,而不會遇到 Android 12+ 的後台麥克風訪問限制。它也不需要從ACTION_BOOT_COMPLETED
廣播接收器手動啟動服務,因此在初始啟動期間不受該廣播延遲的影響。
CAPTURE_AUDIO_OUTPUT
此權限用於錄製VOICE_CALL
音訊串流。如果沒有此系統權限,則無法存取此流以及其他一些流,例如VOICE_DOWNLINK
和VOICE_UPLINK
。
有了這兩個權限,BCR 就可以可靠地偵測電話並記錄通話的音訊串流。錄製過程會提取 PCM s16le 原始音頻,並使用 Android 的內建編碼器來產生壓縮的輸出檔案。
zip 檔案和其中包含的 APK 均經過數位簽署。注意:從版本 1.31 開始,zip 檔案簽章機制從 GPG 切換為 SSH。若要驗證舊版的簽名,請參閱版本 1.30 的README.md
。
若要驗證下載的數位簽名,請按照此處的步驟操作。
首先,從 zip 中提取 apk,然後運行:
apksigner verify --print-certs system/priv-app/com.chiller3.bcr/app-release.apk
然後,檢查 APK 簽章憑證的 SHA-256 摘要是否為:
d16f9b375df668c58ef4bb855eae959713d6d02e45f7f2c05ce2c27ae944f4f9
BCR 可以像大多數其他 Android 應用程式一樣使用 Android Studio 或 gradle 命令列進行建置。
建構 APK:
./gradlew assembleDebug
建置 Magisk 模組 zip(如果需要,它會自動執行assembleDebug
任務):
./gradlew zipDebug
輸出檔寫入app/build/distributions/debug/
。 APK 將使用預設自動產生的偵錯金鑰進行簽署。
若要使用特定簽章金鑰建立發佈版本,請設定下列環境變數:
export RELEASE_KEYSTORE=/path/to/keystore.jks
export RELEASE_KEY_ALIAS=alias_name
read -r -s RELEASE_KEYSTORE_PASSPHRASE
read -r -s RELEASE_KEY_PASSPHRASE
export RELEASE_KEYSTORE_PASSPHRASE
export RELEASE_KEY_PASSPHRASE
然後建置發布 zip:
./gradlew zipRelease
歡迎並非常感謝錯誤修復和翻譯拉取請求!
如果您有興趣實現一項新功能並希望將其包含在 BCR 中,請先開啟一個問題進行討論。我希望 BCR 盡可能簡單且維護成本低,因此我不太傾向於添加任何新功能,但我可以相信並非如此。
BCR 根據 GPLv3 獲得許可。請參閱LICENSE
以取得完整的許可證文字。