Skip to content

[PM-40488] fix: clear clipboard setting does not clear the clipboard on Android 13+ - #7169

Open
mvanhorn wants to merge 3 commits into
bitwarden:mainfrom
mvanhorn:fix/7069-clear-clipboard-not-clearing
Open

[PM-40488] fix: clear clipboard setting does not clear the clipboard on Android 13+#7169
mvanhorn wants to merge 3 commits into
bitwarden:mainfrom
mvanhorn:fix/7069-clear-clipboard-not-clearing

Conversation

@mvanhorn

@mvanhorn mvanhorn commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

🎟️ Tracking

Fixes #7069

📔 Objective

The "clear clipboard" setting does not clear the clipboard on Android 13+. Setting a short interval (e.g. 10 seconds), copying a password, and waiting leaves the secret on the clipboard. Multiple users have confirmed this across recent devices (Samsung Galaxy S25+ on Android 16, Fairphone FP5 on Android 15, Samsung SM-A366B on Android 16) and app versions 2026.2.1 through 2026.6.x.

The clear is scheduled by BitwardenClipboardManagerImpl.setText() as a delayed OneTimeWorkRequest and executed by ClearClipboardWorker.doWork(), which relies solely on clipboardManager.clearPrimaryClip(). On newer Android versions clearPrimaryClip() can be a no-op, so the secret is never evicted.

This change makes ClearClipboardWorker.doWork() clear robustly: it first overwrites the primary clip with an empty, sensitive ClipData (marking ClipDescription.EXTRA_IS_SENSITIVE on API 33+, and the legacy android.content.extra.IS_SENSITIVE key below that, matching the pattern already used in BitwardenClipboardManagerImpl.setText), then calls clearPrimaryClip() (guarded to API 28+) to remove the placeholder entry on devices that honor it. Background clipboard restrictions on Android 10+ gate reads, not writes, so the background overwrite/clear remains permitted. The fix stays entirely within the existing worker, with no scheduling or DI changes.

A unit test (ClearClipboardWorkerTest) covers the Android 13+ path to prevent regression.

📸 Screenshots

Not applicable - this is a background-worker/behavioral fix with no UI surface.


AI was used for assistance.

@mvanhorn
mvanhorn requested review from a team and david-livefront as code owners July 16, 2026 05:26
@bitwarden-bot

Copy link
Copy Markdown
Collaborator

Thank you for your contribution! We've added this to our internal tracking system for review.
ID: PM-40488
Link: https://bitwarden.atlassian.net/browse/PM-40488

Details on our contribution process can be found here: https://contributing.bitwarden.com/contributing/pull-requests/community-pr-process.

@bitwarden-bot bitwarden-bot changed the title fix: clear clipboard setting does not clear the clipboard on Android 13+ [PM-40488] fix: clear clipboard setting does not clear the clipboard on Android 13+ Jul 16, 2026
)
},
)
if (isBuildVersionAtLeast(version = Build.VERSION_CODES.P)) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this version check here?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove this since we now have tests

Remove the isBuildVersionAtLeast(P) gate and the OmitFromCoverage
annotation now that the worker has test coverage.
@mvanhorn

Copy link
Copy Markdown
Contributor Author

Both removed in 5e2465c: the version check is gone and OmitFromCoverage dropped now that the tests cover the worker.

clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
worker = ClearClipboardWorker(
appContext = context,
workerParams = mockk(relaxed = true),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be done inline above?

private val context = RuntimeEnvironment.getApplication()
private val clipboardManager = context.getSystemService<ClipboardManager>()
private val worker: ClearClipboardWorker = ClearClipboardWorker(
    appContext = context,
    workerParams = mockk(relaxed = true),
)

or do these tests require a new instance each time?

Per review, fold the @before setup into property initializers. getSystemService<T>()
returns T?, so the clipboard manager is unwrapped with requireNotNull rather than !!,
which detekt's UnsafeCallOnNullableType rejects.

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
@mvanhorn

Copy link
Copy Markdown
Contributor Author

Pushed a0302ac with the inline setup you suggested.

One deviation from your snippet: context.getSystemService<ClipboardManager>() returns ClipboardManager?, and the tests call it non-null throughout, so as written it does not compile. I unwrapped with requireNotNull(...) rather than !!, since UnsafeCallOnNullableType is active in detekt-config.yml and requireNotNull is already the pattern used elsewhere in the test sources.

On your other two comments, I believe both are already addressed and GitHub has them marked outdated:

  • The isBuildVersionAtLeast(Build.VERSION_CODES.P) guard around clearPrimaryClip() is gone; the call is unconditional now.
  • The code you asked me to drop now that tests cover it is removed.

If either of those is not what you meant, point me at the current line and I will fix it.

Verified locally: :app:testStandardDebugUnitTest --tests "*ClearClipboardWorkerTest*" runs 4 tests, 0 failures, 0 errors; ./gradlew detekt passes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[PM-39184] Clear clipboard setting does not clear clipboard

3 participants