直接回答
把本地模型导入视为一条验证流水线,而不是扩展名检查。Android Storage Access Framework 允许用户通过文档提供方选择文档,并以 content URI 授予访问权限;该 URI 可能指向本地存储、可移动存储、云端提供方或虚拟文档。提供方返回的 MIME 类型和显示名称可作为过滤与界面提示,但不是文件字节的密码学证明,也不能证明运行时兼容。只请求当前导入路径所需的访问权限,并将 URI 授权与任务生命周期绑定,同时处理提供方无法返回稳定数据流的情况。开始加载前,记录保护隐私的元数据:提供方 authority、报告的 MIME、显示名称、可读取的长度、最后修改时间以及是否能打开 descriptor。不要记录用户完整路径或文件正文。选择器可以展示受支持的 MIME 筛选项,但应用必须再次检查用户选中的结果,因为文档提供方可能返回宽泛、缺失或意外的标签。文件名以 `.litertlm`、`.tflite` 或 `.gguf` 结尾,不代表内容一定符合该格式;改名也不会转换格式。应用要通过受限的数据流或 descriptor 读取内容,并处理短读、中断和提供方错误。若需持久化副本,应写入应用拥有的临时文件,设定明确大小上限和取消路径,再使用目标运行时解析器或官方定义的文件标识检查实际格式。不能只凭不可信元数据就解压、反序列化或分配张量。把制品是否符合所选运行时和应用配置列为加载前检查。还要区分来源与有效性:用户选择的文件可能可读但不受支持,可能解析成功但对预期任务过大,也可能成功加载但没有在当前设备验证。就 Cove 相关文案而言,当前 Gemma E2B 配置的 SHA-256 字段为空,因此导入指南不能暗示 Cove 会用官方摘要验证用户选择文件或已交付文件。Cove 下载器仅在配置摘要存在时才会进行可选摘要校验;这个下载路径不等于用户主动导入文档。清理失败时,只能删除应用自己创建的部分临时副本;并先确认它不是已存在的有效模型。不能删用户原始文档,不要不必要地要求全盘存储权限,也不要在新导入通过结构检查与打开测试前覆盖可用模型。只有产品确实需要之后再次打开,并且所选平台 API 路径支持时,才持久化 URI 权限;否则复制到应用私有存储,并在适当时释放选择器授权。稳健的结果状态应区分:权限拒绝、提供方数据流不可用、空文件、文件过大、MIME 不匹配、文件头异常、不支持格式、解析器错误、摘要缺失或不匹配、存储空间不足、取消导入、原生加载失败与导入成功。界面应告诉用户安全的下一步,但不暴露文件内容。用合法的受支持文件、错误扩展名、误导 MIME、截断字节、超限样例、权限撤销、提供方取消、虚拟文档和低存储测试。样例应为合成或获授权数据,并记录捕捉问题的验证层级。不能仅因 Android 返回了一个 URI,就宣称所选文件安全或兼容。完整路径应是:以最小 SAF 合约选择文档;保存元数据和授权状态;在大小受控的前提下写入应用自己的临时位置;独立验证字节与格式;校验任何已配置的摘要;由精确运行时执行打开检查;成功后才移动到最终存储;所有失败路径都清理临时数据。本文提供方法,不声称 Cove 支持任意第三方模型导入或某种特定模型格式。
用户从文档提供方选择一个模型文件
本地 AI 应用增加“导入模型”操作。提供方返回一个看似合理的文件名和 content URI,但 MIME 标签为空或过于宽泛。团队要拒绝损坏或不支持的数据,同时保留用户原件并避免请求过大的存储权限。
决策
把选择器元数据只作为提示,在应用拥有的临时副本中验证字节与运行时格式,并明确管理授权和导入生命周期。
预期结果
应用仅接收通过已配置检查的文件,按确切验证层说明失败,并且不删除、不重写提供方的原始文档。
需要收集的证据
只有将以下证据与同一模型文件和测试运行关联起来,结论才可复核:
- SAF 返回的 URI 与权限状态
- 提供方元数据及实际文件字节
- 格式解析、可选摘要和精确运行时打开结果
收到完整字节流还不够;最终落盘、完整性、运行时加载和恢复路径也必须验证。
实施流程
- 只请求必要的文档选择. 为预期打开操作使用 Storage Access Framework,并仅展示有用的 MIME 筛选项;筛选只是选择器提示,不是安全或格式校验器。
- 记录 URI 授权和提供方元数据. 记录 authority、报告的 MIME、显示名、可读长度和 descriptor 打开结果,不写用户完整路径或内容。处理元数据缺失与提供方停止传输。
- 复制到有限制的临时目录. 读取时限制应用定义的最大大小,并处理取消与空间不足。原 URI 保持不变,临时文件也不能提前替换目前可用模型。
- 验证真实字节与格式. 使用运行时解析器或有文档依据的签名检查,拒绝扩展名/MIME 冲突、截断文件、不支持的文件头及格式错误,之后才交给原生运行时。
- 校验已配置摘要并加载. 存在可信摘要时执行比对,并区分未配置与校验不匹配。用准确运行时打开候选文件;成功后才移动到最终存储,所有失败都清理应用自己的部分副本。
让每个阶段的状态都可观察。失败信息应指出阶段、模型、运行时和恢复动作,同时避免记录用户私密内容。
验收标准
- 提供方 MIME、文件名、URI 授权、字节校验、解析器支持、可选摘要和运行时加载均作为独立检查记录。
- 格式错误、截断、过大、授权撤销、取消和不支持的样例安全失败,且不删除提供方原始文档。
- 只有应用创建的临时副本可以替换或删除;导入失败时保留现有可用模型状态。
- 没有相应配置和测试证据时,不宣称支持任意模型或已验证 Cove 模型摘要。
决策对照表
| 维度 | 选择或证据 | 解读方式 |
|---|---|---|
| 选择器 MIME 筛选 | 用提供方元数据帮助用户选择 | 只是选择提示,不能证明实际字节或格式兼容。 |
| 字节与解析器校验 | 用有资源限制的方式检查实际数据流 | 可以拒绝损坏或不支持内容,但不证明设备性能。 |
| 已配置的可信摘要 | 与独立可信的预期值对比 | 仅在预期摘要确实存在并完成验证时,才确认文件身份。 |
| 运行时打开和任务测试 | 使用选定运行时与固定样例 | 支持限定的导入/加载结论,不代表任意设备或任务都受支持。 |
需要避免的失败模式
- 只看文件扩展名
- 请求宽泛存储权限
- 导入失败时删除用户原文件或覆盖现有模型
还要避免静默回退、版本未固定、缺少取消处理,以及把不同配置的结果混在一起下结论。失败运行也应与成功记录一并保存。
最小复现记录
| 层级 | 记录内容 |
|---|---|
| 设备 | 厂商、型号、芯片、内存档位、操作系统版本 |
| 软件 | 应用版本和 Git 提交号 |
| 模型 | 系列、变体、版本、格式、文件长度、摘要、量化方式 |
| 运行时 | 名称、版本、请求后端及实际后端证据 |
| 工作负载 | 样例版本、输入摘要、提示词摘要、输出策略 |
| 结果 | 完成、失败、取消、回退及保护隐私的诊断信息 |
发布检查表
- ☐ 主要搜索问题已回答,且没有未经支持的数字。
- ☐ 所有模型文件和运行时版本均已固定。
- ☐ 代表性任务和失败处理策略均有说明。
- ☐ 生命周期、取消、清理和回退均已测试。
- ☐ 用户内容与网络边界均有说明。
- ☐ 结论只适用于被记录的配置。
- ☐ 新增测试结果时链接到原始方法或证据。
来源与相关证据
- Android privacy and security
- Android Developers — 使用 Storage Access Framework 访问文档
- Android API — ContentResolver.getType(Uri)
- Android API — ContentResolver.openFileDescriptor(Uri,String)
- Cove ModelDownloader 摘要及临时文件行为
中文部署与排错专题见。奇连 AI 端侧专题。
常见问题
文档 MIME 类型写着模型文件,是否就安全?
不是。MIME 是提供方提供的元数据。应先用有限制的方式检查实际字节并解析格式,再将文件交给原生运行时。
扩展名筛选能拦下所有非法模型吗?
不能。扩展名和选择器 MIME 可改善选择体验,但可能错误或缺失。选中文件后仍要验证实际内容和运行时支持格式。
应用应该永久保留选中文档的 URI 权限吗?
只有之后确实需要重新打开该提供方文档,且平台 API 流程支持持久化授权时才需要。否则复制到应用自有存储,并在适当时释放权限。
Cove 导入时会校验 Gemma E2B 的文件摘要吗?
当前 Cove 配置中的 Gemma E2B SHA-256 字段为空,因此网站不能宣称该制品会与可信摘要进行校验。