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
以获取完整的许可证文本。