MediaScratchFileProvider stores temporary media under the application's cache directory, but it has no general cleanup mechanism. Most attachment-oriented users explicitly delete scratch-space URIs when their owner is discarded or replaced by permanent telephony parts. External preview, sharing, and vCard-import handoffs can create scratch copies without an application-controlled deletion path, leaving those files until Android happens to evict the application cache. Android documentation says to not rely on this system cache evict behavior (https://developer.android.com/training/data-storage/app-specific#internal-remove-cache) and that caches should be explicitly managed
Current behavior
UriUtil.persistContentToScratchSpace() creates a new scratch-space URI and returns it without associating an owner, purpose, expiration, or cleanup callback with the file. In practice, cleanup is caller-owned: each caller is responsible for deleting its scratch-space URI through the content resolver once the file is no longer needed.
Existing cleanup behavior
Missing terminal cleanup
External activities may read a granted URI asynchronously, so deleting these files immediately after startActivity() would be unsafe. However, relying solely on opportunistic cache eviction means repeated previews, shares, and contact imports can accumulate private copies indefinitely from the application's perspective.
Android's app-specific storage guidance explicitly says applications should maintain their own internal cache files rather than rely on the system to clean them up.
Impact
- A vCard scratch copy can retain names, phone numbers, email addresses, and other contact data after the import activity has finished and after the originating message or conversation is deleted.
- Preview and sharing scratch copies can retain images, documents, or other attachment content after the receiving activity has finished and after the original source is deleted.
- The files remain in application-private credential-encrypted cache storage, limiting ordinary cross-application exposure, but that does not make retaining content beyond its useful or user-expected lifetime desirable.
Proposed approach
- Add a bounded cleanup mechanism for orphaned
MediaScratchFileProvider files.
- Keep active draft, pending-message, sending-message, and retryable failed-message attachments until they are discarded or replaced by permanent telephony parts.
- Delete files immediately where completion is observable, preserving the existing conversion and attachment cleanup behavior.
- Mark or isolate external-handoff files so they can be pruned conservatively by age after a grace period long enough for external consumers to finish reading them.
- Run orphan cleanup at a safe maintenance point such as application startup, excluding files referenced by known active owners.
- Make cleanup idempotent and tolerate files already removed by Android's cache eviction.
MediaScratchFileProviderstores temporary media under the application's cache directory, but it has no general cleanup mechanism. Most attachment-oriented users explicitly delete scratch-space URIs when their owner is discarded or replaced by permanent telephony parts. External preview, sharing, and vCard-import handoffs can create scratch copies without an application-controlled deletion path, leaving those files until Android happens to evict the application cache. Android documentation says to not rely on this system cache evict behavior (https://developer.android.com/training/data-storage/app-specific#internal-remove-cache) and that caches should be explicitly managedCurrent behavior
UriUtil.persistContentToScratchSpace()creates a new scratch-space URI and returns it without associating an owner, purpose, expiration, or cleanup callback with the file. In practice, cleanup is caller-owned: each caller is responsible for deleting its scratch-space URI through the content resolver once the file is no longer needed.Existing cleanup behavior
deleteTemporaryAttachment().MessagePartData, and multi-conversation sends create a separate scratch copy for each additional outgoing message; after ownership transfers to the message, these copies follow the normal draft and outgoing-MMS cleanup lifecycle.MessagePartData.destroySync()deleting only application-owned scratch-space URIs.Missing terminal cleanup
file:URI into scratch space before launchingACTION_VIEW, without deleting the copy afterward.file:URI into scratch space before launching the chooser, without deleting the copy afterward.file:URI by creating a scratch copy; forwarding can transfer ownership to an outgoing message, but the external share path has no visible terminal cleanup.External activities may read a granted URI asynchronously, so deleting these files immediately after
startActivity()would be unsafe. However, relying solely on opportunistic cache eviction means repeated previews, shares, and contact imports can accumulate private copies indefinitely from the application's perspective.Android's app-specific storage guidance explicitly says applications should maintain their own internal cache files rather than rely on the system to clean them up.
Impact
Proposed approach
MediaScratchFileProviderfiles.