直接回答
Android API 24 起支持跨应用多窗口拖放。这是用户发起的内容引用传递,不代表接收应用拥有永久文件。来源端必须设置 DRAG_FLAG_GLOBAL 才能跨应用窗口拖动;如需传递 content URI,还要通过 DRAG_FLAG_GLOBAL_URI_READ 或 DRAG_FLAG_GLOBAL_URI_WRITE 提供相应模式。接收 Activity 在 ACTION_DROP 调用 requestDragAndDropPermissions(event),但该方法只接受事件,不能由接收方选择读写模式;权限取决于发送方 flags。成功取得 grant 也不证明内容格式有效或允许后续推理。 拖放事件有两个概念不同的数据通道。ClipData 是可传递的内容描述,可包含一个或多个项目,例如 content URI。DragEvent.localState 是来源侧进程内状态:Android 文档明确它只对发起拖放 Activity 中的 View 可用,在其他 Activity 中返回 null。跨应用接收方不得依赖藏在 localState 中的对象引用、数据库实体、回调或信任标记。可用 ClipDescription 判断应用是否接受声明的 MIME 家族,但这个标签只是提示;provider 元数据与实际文件字节仍要独立校验。不能从 URI 字符串、MIME 类型、标题或拖放阴影推断用户身份或授权。 普通 DragAndDropPermissions grant 是临时授权,绑定请求它的 Activity。requestDragAndDropPermissions(event) 可能因事件没有 content URI 或无法授予而返回 null;应独立检查应用是否已有其他途径建立的访问,不能从 null 推断。需要长期授权时,来源端必须同时提供 DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION 与 URI 读/写 flags;接收方还必须针对收到的 URI 和被授予模式调用 ContentResolver.takePersistableUriPermission,并确认调用成功。仅有来源 flag 不会自动令接收应用的授权持久。持久 grant 需独立记录与释放,不属于本文普通临时授权流程。不要凭空扩大读写模式。 普通 DragAndDropPermissions 授权绑定到调用请求的 Activity。Android 说明,该 Activity 销毁或调用 release() 时,权限会被撤销,以先发生者为准。权限对象应由接收 Activity 或生命周期相同的组件拥有;任务不再需要 URI 时,包括取消和拒绝路径,都应释放。若把 URI 转给另一个 Activity,Android 要求转交 ClipData 和对应 URI grant flag;否则应在交接前完成有界复制。授权与已打开 descriptor 是不同资源:要实测 descriptor 行为,不能断言 release 会立即终止所有正在读取的数据。 如果任务可能在拖放页面关闭后继续,应趁 Activity grant 有效时,把所选字节复制到应用拥有且有上限的临时文件。读取时限制总字节数、支持取消并处理短读;不要信任 provider 报告的大小或显示名称。处理前用目标 decoder/parser 验证真实格式,并限制图片尺寸或文档结构,拒绝不支持的内容。URI 可能指向云端或虚拟 provider、读取缓慢、没有数据流或中途失败。要分开管理原始 URI、临时副本、解析值和模型输入。拒绝或取消时只删除应用创建的临时数据,不能删除来源 provider 的原始文档。 用户 drop 表达了把项目交给这个应用的意图,但不一定同意每个后续操作。如果应用将运行本地推理、保存历史、保留副本或分享派生文本,适当时应把它们作为独立产品决策。端侧推理不会自动保护导入路径:内容 provider、副本、缓存、日志、备份和后续导出都有自己的边界。使用隐私安全的合成夹具;URI path 也要脱敏,因为 authority 和文档名可能泄露账号或个人上下文。诊断记录仅保留不透明 fixture ID、MIME 提示、必要时安全的 provider 类别、字节数范围、结果分类和生命周期转换。 测试矩阵应围绕真实平台事件,而不是仅调用一个直接返回可读文件的 helper。用独立来源 Activity 和目标 Activity 验证 localState 不会跨 Activity 可用。覆盖 ACTION_DROP 只读 grant、无 URI、多项 ClipData、MIME 不匹配、权限请求返回 null、provider 打开失败、延迟读取、部分 stream、超大内容、损坏字节、Activity 销毁、显式 release 与取消。授权请求和 provider 打开结果要分开观察。合成本地文件可以验证流读取边界和 parser,却不能证明 Android 真的交付了预期跨应用 grant。双应用仪器测试只支持所测 Android 版本、provider、flags 和生命周期顺序,并不证明 Cove 当前实现了拖放导入。
用户把文档应用中的扫描件拖入 AI 审阅页
目标 Activity 从另一个可见应用收到 ClipData 项。URI 看起来像 PDF,但来源可能是云端 provider,分析任务也可能超过最初的 drop 回调周期。
决策
只使用发送端 flags 提供的访问模式;任务若会超出 Activity 生命周期,就在临时授权有效时复制有界字节,或另行成功取得已提供的持久授权,再校验内容并单独确认后续处理。
预期结果
应用能分别报告传输、格式校验和请求任务是否完成,不把 MIME 标签或成功 drop 当作内容有效或永久权限的证明。
需要收集的证据
只有将以下证据与同一模型文件和测试运行关联起来,结论才可复核:
- ANDROID-DRAG-DROP-URI-2026-10:拖放 URI 授权与 DragAndDropPermissions 生命周期以 Android API 合同为准。
- ANDROID-PRIVACY-01:应用仍负责数据访问、存储和用户控制。
- Android 多窗口拖放指南与 DragEvent/DragAndDropPermissions API 描述跨 Activity 的 ClipData、localState 和授权撤销边界。
隐私声明需要数据清单和流量证据;“本地模型”标签本身不是证明。
实施流程
- 定义接受的拖放项目. 列出支持的 ClipData 项目形态、MIME 提示、字节与尺寸上限,以及是否允许多项。清楚提示并拒绝不支持的项目;不要把 localState 当作跨应用元数据。
- 按发送端 flags 获取 ACTION_DROP 授权. 来源端必须设置 DRAG_FLAG_GLOBAL 和符合任务需要的 URI 读/写 flags。目标端调用 requestDragAndDropPermissions(event),不能自行选择授权模式;null 时不要扩大存储访问。
- 区分临时与持久授权. 默认使用 Activity 绑定的临时 grant。需要持久授权时,发送端还须提供 persistable flag;接收端必须成功调用 takePersistableUriPermission,并单独记录和释放该 grant。
- 在授权和任务生命周期内读取. 明确普通临时权限对象的所有者。打开 provider stream,以字节上限、取消和短读处理进行复制;不再需要时释放。分开测试 Activity 销毁与已打开 descriptor 的行为。
- 验证并处理应用私有副本. 用目标格式 parser 检查字节、尺寸或结构上限及任务策略。临时文件、解析数据和模型输入分别管理;除非独立产品决策要求,否则不长期保留。
- 测试真实跨 Activity 场景. 用两个测试应用或 Activity 覆盖 localState 隔离、正常和缺失授权、provider 错误、多项目、损坏内容、取消、重建、release 与清理。普通日志不包含 URI 或内容。
让每个阶段的状态都可观察。失败信息应指出阶段、模型、运行时和恢复动作,同时避免记录用户私密内容。
验收标准
- 接收端使用 ClipData 传递跨应用内容,不依赖其他 Activity 无法取得的来源端 DragEvent.localState。
- 发送端提供 DRAG_FLAG_GLOBAL 和所需 URI 模式;接收端通过 DragEvent 取得发送方已提供的授权,null 结果不触发扩权。
- 接收端不能在 requestDragAndDropPermissions 中选择读写模式;普通 grant 绑定 Activity,持久 grant 需发送端 flag 且成功调用 takePersistableUriPermission。
- 分别测试授权所有者、Activity 销毁、显式 release、provider stream 生命周期、应用副本清理和后续处理。
- MIME 与显示元数据不能替代有界字节读取和真实格式校验;drop 本身不表示同意推理、持久化或导出。
- 内容是通用 Android 方法,不声称 Cove 已支持拖放内容导入。
决策对照表
| 维度 | 选择或证据 | 解读方式 |
|---|---|---|
| 同一 Activity 内拖放 | 只用 localState 做来源进程内协调,并走预期本地数据路径。 | localState 不是跨进程传输合同,在其他 Activity 中为空。 |
| 跨应用 URI 拖放 | 来源端设置 DRAG_FLAG_GLOBAL 与 URI 模式;接收端通过事件读取 ClipData 并取得 grant。 | 接收端不能选择读写模式;普通授权绑定 Activity,也不证明字节、格式或后续处理获准。 |
| 系统文件选择器 | 使用选择器合约和该入口明确规定的 URI 授权行为。 | 这是不同入口,不能复用拖放事件的权限申请方式。 |
| 长时间任务 | 在 Activity grant 结束前把有界字节复制到应用临时存储。 | 副本成为另一个应用拥有的制品,必须有独立清理和保留策略。 |
需要避免的失败模式
- 把 localState 当成跨进程传递的内容或身份凭据。
- 把成功 drop、MIME label 或 URI 字符串当作内容有效与授权完成的证明。
- 在 Activity grant 结束后继续依赖原 URI,或仅因请求返回 null 就索取宽泛存储权限。
还要避免静默回退、版本未固定、缺少取消处理,以及把不同配置的结果混在一起下结论。失败运行也应与成功记录一并保存。
最小复现记录
| 层级 | 记录内容 |
|---|---|
| 设备 | 厂商、型号、芯片、内存档位、操作系统版本 |
| 软件 | 应用版本和 Git 提交号 |
| 模型 | 系列、变体、版本、格式、文件长度、摘要、量化方式 |
| 运行时 | 名称、版本、请求后端及实际后端证据 |
| 工作负载 | 样例版本、输入摘要、提示词摘要、输出策略 |
| 结果 | 完成、失败、取消、回退及保护隐私的诊断信息 |
发布检查表
- ☐ 主要搜索问题已回答,且没有未经支持的数字。
- ☐ 所有模型文件和运行时版本均已固定。
- ☐ 代表性任务和失败处理策略均有说明。
- ☐ 生命周期、取消、清理和回退均已测试。
- ☐ 用户内容与网络边界均有说明。
- ☐ 结论只适用于被记录的配置。
- ☐ 新增测试结果时链接到原始方法或证据。
来源与相关证据
- Android privacy and security
- Android Developers — 多窗口模式中的拖放
- Android API — DragAndDropPermissions 生命周期与 release
- Android API — DragEvent 的 ClipData 与 localState
- Android API — Activity 授权请求与可空结果
- Android API — ContentResolver 持久 URI grant
- Android API — View 拖放标志与 URI 访问
中文部署与排错专题见。奇连 AI 端侧专题。
常见问题
关闭页面后,拖入的 content URI 还能读取吗?
普通 DragAndDropPermissions 授权绑定到申请它的 Activity,Activity 销毁或调用 release() 时会撤销。需要后续字节时应在授权有效时复制;持久授权则另需发送端 flag 和成功调用 takePersistableUriPermission。
DragEvent.localState 能把对象传给另一个应用吗?
不能。Android 文档说明 localState 只提供给发起拖放 Activity 中的 View;其他 Activity 会得到 null。应通过 ClipData 传递项目,并校验收到的全部值。
接收端能在 requestDragAndDropPermissions 中选择读或写吗?
不能。方法只接受 DragEvent;URI 读写模式由发送端 startDragAndDrop 的 flags 决定。持久 flag 也只是提供该选项,接收端必须另行成功调用 takePersistableUriPermission。
用户拖入文件是否就同意 AI 处理?
这个手势表示将项目交给接收应用,不代表用户一揽子同意保留、模型处理、保存历史或后续分享。应按产品合同将这些后续操作明确区分。