khushalx/core-alert

GitHub: khushalx/core-alert

一款 Android 优先的个人安全紧急响应应用,通过隐蔽的物理快捷键触发 SOS,向监护人发送实时位置与证据并协调响应。

Stars: 1 | Forks: 0

Core Alert logo

Core Alert

分秒必争。

一款 Android 优先的个人安全应用,用于向受信任的监护人发送警报、 分享进行中的紧急事件并协调响应。

Core Alert 是一款围绕简单紧急流程构建的黑客松原型:受保护的用户手动按下或连续五次按下音量减小键启动 SOS,通过短暂的倒计时进行确认,并创建一个由云端支持的 incident。指定的监护人随后可以接收警报、跟踪位置更新、查看可用的证据片段,并确认是否进行响应。 ## 项目状态 | 领域 | 当前状态 | | --- | --- | | 产品阶段 | 功能完备的 Android 优先黑客松原型 | | 客户端 | React Native 0.86 和 Expo SDK 57,包含自定义的原生 Android 项目 | | 原生安全引擎 | Kotlin 无障碍、倒计时、生命周期、位置、证据和 WorkManager 组件 | | 后端 | Supabase Auth、Postgres、Realtime、Storage、行级安全、RPC 和 Edge Functions | | 分发 | 存在本地 Android 和 EAS 配置;生产环境签名和应用商店发布尚未完成 | | Expo Go | 不支持;该应用依赖于自定义的原生 Android 代码 | ## 功能矩阵 ### 代码库中已实现 | 功能 | 已实现内容 | | --- | --- | | 身份验证 | Supabase 电子邮件/密码注册、登录、密码重置、会话恢复和退出登录 | | 紧急情况配置 | 受保护用户配置,包含联系人和医疗背景信息,由 Supabase 支持并提供本地恢复支持 | | 监护人绑定 | 邀请码、接收请求、接受/拒绝、关联账户和仅限联系人监护人、主监护人选择以及移除 | | 手动和演示 SOS | 手动激活、可取消的倒计时、云端 incident 创建以及清晰标识的 Demo SOS 模式 | | 五次按键快捷方式 | 范围严格限定的 Android `AccessibilityService`,使用单一原生序列管理器在三秒内计算五次独立的音量减小按键 | | UI 关闭时激活 | 挂起的原生激活存储和锁屏倒计时 activity 可以在不依赖 React Native UI 存活的情况下启动流程 | | Incident 一致性 | 持久化的生命周期状态、UUID 激活 ID、幂等的创建/恢复和解决 RPC,以及针对重复活动 incident 的数据库保护 | | 实时位置 | 活动 incident 前台/后台位置更新、本地重试队列、incident 历史记录以及监护人地图/路线访问 | | 监护人响应 | Incident 视图、响应/无法响应操作、呼叫/消息切换、传递状态以及 Supabase Realtime 更新 | | 证据捕获 | 原生 CameraX MP4 视频(含麦克风音频)、原生 AAC/M4A 回退、私有应用存储、前台信息披露和 WorkManager 上传 | | 证据访问 | 私有 Supabase Storage、证据元数据、Realtime 更新,以及为所有者和指定监护人提供的短期签名链接 | | Incident 解决方案 | 幂等解决、服务和通知清理、本地状态重置,以及应用重启后的活动 incident 恢复 | | 活动和诊断 | Incident 历史记录、incident 详情、生命周期诊断、快捷方式诊断、权限状态和可恢复的警告 | | 响应者模拟 | 仅用于演示的响应者仪表板,标记为 **HACKATHON SIMULATION**,没有警方连接或调度声明 | ### 已实现但依赖于外部配置 | 功能 | 当前限制 | | --- | --- | | 监护人推送通知 | Token 注册、仅限监护人的高音量 Android 渠道、振动、操作、Edge Function 传递和传递记录均已存在。此检出不包括 Firebase Android 客户端配置或 FCM v1 服务账号凭证,因此在配置这些内容之前,干净的构建版本不应期望实现应用外 Android 推送。 | | SMS 回退 | 存在安全的服务器端 Twilio 集成,默认处于禁用状态。它需要提供商凭证、经过批准的发送号码、部署密钥以及 `SMS_FALLBACK_ENABLED=true`。 | | 无响应升级 | 存在升级 Edge Function 和 cron 设置模板。它们需要部署的 functions、共享的 cron 密钥和已配置的 Supabase Cron/`pg_net` 作业。 | | 后台快捷方式可靠性 | 无障碍实现不需要 JavaScript,但 Android 和制造商的电源策略可能仍会停止或限制它。用户必须手动启用该服务。 | | 摄像头和麦克风证据 | 录制需要明确的权限,并且在前台服务启动之前需要有可见的倒计时 activity。摄像头、麦克风、存储或操作系统限制可能会在不取消 SOS 的情况下导致证据不可用。 | | APK/发布交付 | 存在 EAS 配置和 Gradle 发布输出,但当前发布变体使用 debug 签名配置,不适用于 Play Store 生产环境。 | ### 未实现 - 自动化的警察、救护车、消防或政府紧急调度 - 连续的实时摄像头或实时音频流;监护人接收的是已完成的准实时证据片段 - 保证的推送、SMS、Realtime、位置、后台服务或证据传递 - Android **强制停止** 后的运行 - 硬件快捷方式和 Android 前台服务的完全原生 iOS 平等性 - Expo 推送回执对账和提供商传递回调 - 生产环境 Play Store 签名、合规性审查和发布强化 ## 紧急生命周期 Core Alert 使用单一的持久化生命周期,而不是让屏幕和服务各自独立决定 SOS 是否处于活动状态: ``` IDLE → COUNTDOWN → ACTIVATING → ACTIVE → ENDING → RESOLVED → IDLE ``` 当无法确认关键的云端操作时,激活和结束可以转入显式的失败状态。次要故障(例如位置更新、监护人传递或证据启动失败)会被记录下来,而不会创建第二个 incident 或默默地将用户返回到 `IDLE`。 原生层和数据库层使用相同的激活 UUID 以确保重试安全。Supabase 迁移添加了唯一的激活标识符和部分唯一索引,允许每个受保护用户最多拥有一个活动 incident。 ## 音量减小五次快捷方式 物理快捷方式完全使用原生 Android 代码实现: 1. 用户启用 Protection Mode 并手动授予 Core Alert 无障碍权限。 2. `CoreAlertAccessibilityService` 接收音量键事件,而不检查屏幕内容。 3. `CoreAlertVolumeSequenceManager` 仅计算非重复的音量减小 `ACTION_DOWN` 事件。 4. 三秒内按下五次将创建一个挂起的激活,有效期为 60 秒。 5. 原生紧急倒计时 activity 打开,包括在设备允许的情况下覆盖锁屏。 6. 倒计时完成后,将激活移交给中央 SOS 协调器。 7. 协调器创建或恢复一个 Supabase incident 并启动 incident 服务。 该快捷方式适用于应用打开、处于后台或从最近任务中移除的情况,以及 Android 允许的锁定屏幕状态。在手动 **强制停止**、禁用无障碍功能、禁用保护或操作系统/OEM 终止服务后,它将无法工作。 ## 监护人警报 发送者和监护人使用不同的通知行为: - 受保护用户的激活和前台服务通知是静默状态通知,带有可选的短暂触觉确认。 - 绑定的监护人接收到传入的 SOS 时,使用专用的最高重要性监护人渠道,包含声音、振动、浮动通知展示、公共锁屏可见性和响应操作。 - 通知操作可以将监护人标记为正在响应或无法响应,或者打开实时位置视图。 - 传递尝试和确认将存储在 Supabase 中,并通过 Realtime 公开。 应用路径已存在,但远程 Android 传递仍需要 [外部服务设置](#external-service-setup) 中描述的外部 FCM 配置。 ## 证据模型 证据录制是原生的,不依赖于 JavaScript runtime 保持存活。 1. 可见的倒计时结束后,Android 将启动证据前台服务。 2. 当权限和硬件均可用时,CameraX 使用其自己的麦克风轨道录制 MP4 视频。 3. 如果无法启动视频,原生 `MediaRecorder` 将回退到 M4A/MP4 容器中的 AAC 音频。 4. 完成的短片段(目前为 30 秒)将写入私有应用存储。 5. 唯一的 WorkManager 作业将文件上传到私有的 `incident-evidence` Supabase Storage bucket。 6. 该 incident 分配的所有者和监护人可以请求短期签名 URL。 7. 只有在服务器确认上传完成后,才会删除本地片段。 Core Alert 绝不会尝试同时运行两个麦克风录制器,绝不会在没有 runtime 权限的情况下开始录制,并且如果证据捕获失败,会保持 SOS 活动状态。证据是准实时的分段媒体,而不是连续的视频通话或实时麦克风源。 ## 架构 | 层 | 职责 | | --- | --- | | React Native / Expo Router | 身份验证、新手引导、个人资料、监护人管理、incident 屏幕、诊断和常规应用导航 | | 应用上下文和服务 | 会话恢复、生命周期协调、incident API 调用、Realtime 订阅、通知处理、位置排队和 UI 状态 | | 原生 Android / Kotlin | 物理快捷方式、挂起激活、锁屏倒计时、前台保护、原生激活、活动位置、证据录制、安全设备凭证和上传调度 | | Supabase Postgres | 配置文件、监护人关系、incidents、分配、位置、传递尝试、确认、升级事件、设备注册和证据元数据 | | Supabase Realtime | Incident 状态、位置、监护人确认、通知传递、分配和证据可用性 | | Supabase Storage | 具有策略控制访问权限的私有 incident 证据对象 | | Supabase Edge Functions | 经过身份验证的通知传递、原生设备激活、原生保护凭证、原生证据上传授权和计划的升级 | ### 仓库结构 ``` app/ Expo Router screens and routes components/ Shared interface components store/ Authentication, app, and SOS contexts services/ Supabase, incident, guardian, location, notification, hardware, and lifecycle services modules/core-alert-hardware/ React Native bridge for native diagnostics/state android/ Native Android app, services, activities, and workers supabase/migrations/ Database schema, RLS, RPC, Realtime, and Storage changes supabase/functions/ Edge Functions and shared delivery logic supabase/templates/ Optional escalation scheduler template supabase/tests/ Database policy tests assets/ Logo, icons, fonts, and splash assets ``` ## 安全与隐私 - Supabase 行级安全将 incident、位置、传递、确认和证据访问权限限制为受保护的用户和指定的监护人。 - 证据使用私有的 Storage bucket;客户端通过过期的签名 URL 访问对象,而不是公共对象 URL。 - 原生保护凭证在设备上使用 Android Keystore 进行加密,并且服务器端仅存储哈希值。 - Supabase 服务角色密钥和 SMS 提供商凭证仅属于 Edge Function 密钥。 - 移动客户端使用来自未跟踪的本地环境文件的公共 Supabase URL 和 anon/publishable key。 - 摄像头、麦克风、通知、无障碍和位置权限是明确请求的。 - 活动的原生录制和位置工作使用前台服务披露通知。 - 无障碍处理仅限于硬件按键事件,不检查或存储屏幕内容。 - 当证据或次要传递渠道失败时,SOS 可能会继续;应用不会将该失败呈现为已成功传递。 ## 前置条件 - macOS、Linux 或 Windows,且带有与 Expo SDK 57 兼容的 Node.js/npm - Android Studio 和 Android SDK 36 - JDK 17 或更高版本 - Android 设备或模拟器 - Supabase 项目和 Supabase CLI - 用于独立 Edge Function 授权测试的 Deno - 可选:用于云端构建的 Expo/EAS 账户 - 可选:用于真实 Android 推送传递的 Firebase/FCM 凭证 - 可选:用于 SMS 回退的 Twilio 凭证 使用 [Expo SDK 57 文档](https://docs.expo.dev/versions/v57.0.0/) 获取特定版本的工具指南。 ## 本地设置 ### 1. 安装依赖 ``` npm ci ``` ### 2. 配置公共移动环境 ``` cp .env.example .env.local ``` 在 `.env.local` 中设置以下值: ``` EXPO_PUBLIC_SUPABASE_URL= EXPO_PUBLIC_SUPABASE_ANON_KEY= ``` 不要将 Supabase 服务角色密钥、FCM 服务账号密钥、Twilio 凭证或 cron 密钥放在客户端环境变量中,也不要将它们提交到 Git。 ### 3. 配置 Java 和 Android 将 `JAVA_HOME` 指向 JDK 17+ 安装路径,并将 `ANDROID_HOME` 指向 Android SDK 36: ``` export JAVA_HOME="" export ANDROID_HOME="" ``` ### 4. 应用 Supabase 后端 进行身份验证并链接本地项目,而不提交特定于项目的凭证: ``` supabase login supabase link --project-ref supabase db push supabase functions deploy ``` 迁移将创建数据库表、RLS 策略、RPC、Realtime 发布、唯一性保护、证据元数据以及应用使用的私有 Storage bucket。 ### 5. 运行 Android 应用 此项目需要原生开发构建;它无法在 Expo Go 中运行。 ``` npm run android ``` 要直接使用 Gradle: ``` cd android ./gradlew assembleDebug ``` 对于连接到 Metro 的 debug 构建: ``` npm start -- --dev-client ``` ## 外部服务设置 ### Android 推送通知 Core Alert 注册 Expo push token,并通过 Expo Push API 发送监护人 payload。在 Android 上,最终的传递路径仍然依赖于 Firebase Cloud Messaging。 当前的检出**不包括**: - 用于应用程序包的 Firebase Android 客户端配置 - EAS/构建环境中的 FCM v1 服务账号凭证 在测试应用外监护人警报之前,请为选定的原生/EAS 构建管道安全地配置这两者。切勿提交服务账号 JSON 文件。在验证通知渠道更改时请重新安装应用或清除其数据,因为 Android 渠道的声音和重要性设置是持久的。 请参阅 [Expo SDK 57 通知指南](https://docs.expo.dev/versions/v57.0.0/sdk/notifications/) 以获取匹配版本的设置。 ### SMS 回退 将仅限服务器的示例复制到被忽略的本地 function 环境文件中: ``` cp supabase/functions/.env.example supabase/functions/.env ``` 在该未跟踪的文件中配置 `SMS_FALLBACK_ENABLED` 和 `TWILIO_*` 变量,然后将它们存储为 Supabase Edge Function 密钥: ``` supabase secrets set --env-file supabase/functions/.env ``` SMS 行为是可选的,并受提供商账户、区域规则、接收者同意、发送者批准和运营商传递的限制。 ### 无响应升级 `process-incident-escalations` 可以为没有响应确认的活动 incident 重试监护人传递。配置一个强健的 `ESCALATION_CRON_SECRET` 作为 Edge Function 密钥,并使用 `supabase/templates/setup_escalation_cron.sql` 作为 Supabase Cron 和 `pg_net` 的已审查模板。 请勿将 cron 密钥或私有项目 URL 提交到模板中。 ## Android 激活检查清单 在物理 Android 设备上安装原生构建后: 1. 登录并完成紧急情况个人资料。 2. 绑定至少一个监护人账户。 3. 在 Core Alert 中启用 Protection Mode。 4. 在 Android 设置中启用 Core Alert 无障碍服务。 5. 允许通知和所需的位置权限。 6. 如果需要证据,请允许摄像头和麦克风权限。 7. 在设备提供该选项的情况下,将 Core Alert 排除在激进的电池优化之外。 8. 首先测试手动 SOS,然后测试在三秒内按下五次音量减小键。 在使用 Android 设置 → 应用 → Core Alert → **强制停止** 后,请勿验证快捷方式;Android 会故意阻止该应用,直到用户再次启动它。 ## 验证 ### 静态和单元检查 ``` npm run lint npm run typecheck npm test ``` Jest 套件涵盖核心服务、生命周期幂等性、通知行为、硬件触发适配器和迁移预期。 ### Supabase 策略和函数检查 在本地 Supabase 堆栈运行的情况下: ``` supabase test db ``` 使用 Deno 运行 Edge Function 授权测试: ``` deno test \ supabase/functions/send-sos-notifications/authorization_test.ts \ supabase/functions/process-incident-escalations/authorization_test.ts ``` ### 手动双设备验证 自动化测试无法证明物理设备的传递。在演示或发布之前,请使用单独的受保护用户和监护人设备验证: - 一次手动或硬件触发准确创建一个 incident; - 发送者看到即时的本地激活状态,而没有响亮的监护人铃声; - 监护人接收到带有声音和振动的应用外锁屏警报; - 通知操作更新 incident 确认状态; - 位置更新仅针对所有者和指定的监护人显示; - 完成的证据片段可通过过期链接获取; - 解决 incident 会停止仅针对 incident 的服务并清除活动通知; - 当配置后,Protection Mode 在单个 SOS 结束后保持启用状态; - 应用重启和临时的连接丢失会恢复一个活动 incident,而不会重复传递。 ## 已知限制 - 在提供 Firebase Android 配置和 FCM v1 凭证之前,干净的检出尚未准备好进行远程监护人推送。 - `app.json` 中提交的自适应图标路径目前包含一个拼写错误。 - 无障碍和前台服务行为因 Android 版本和设备制造商而异。 - Android 手动 **强制停止** 会禁用应用关闭触发器,直到再次打开应用。 - 证据显示为完成的短片段,而不是连续的实时流。 - 摄像头、麦克风、后台位置和通知取决于权限和策略。 - Expo Push API 工单接受并不证明最终设备传递;未实现推送回执对账。 - 未实现 Twilio 传递状态回调。 - 当前的 Gradle release 构建使用 debug 签名。 - 尚未针对 iOS 实现原生的紧急功能。 - 该项目仍然依赖于网络连接和正确部署的 Supabase 服务以进行云端 incident 协调。 ## 技术 - React Native 0.86 - Expo SDK 57 和 Expo Router - TypeScript - Kotlin 和原生 Android 服务 - CameraX 和 `MediaRecorder` - Android WorkManager - Expo Location、TaskManager 和 Notifications - Supabase Auth、Postgres、Realtime、Storage、RPC 和 Edge Functions - Jest 和 pgTAP ## 安全声明 Core Alert 可帮助向受信任的监护人传达紧急情况;它不能替代当地的紧急服务、专业的安全设备或官方的响应系统。发送的请求、接受的推送工单、排队的 SMS、显示的地图或打开的拨号程序并不能证明另一个人或服务已收到或对紧急情况采取了行动。 对于 Demo SOS incidents,所有的响应者活动都是为黑客松展示而模拟的。除非添加了官方集成并得到独立确认,否则绝不能将此应用表述为已通知警方。 ## 许可证 请参阅 [LICENSE](./LICENSE) 以获取随附于此仓库的许可文本。
标签:Android应用, Kotlin, React Native, SOS求助, Supabase, 个人安全, 实时定位, 库, 应急响应, 自动化攻击