androidclipboardmanagerandroid-app-ops

SecurityException: <<other_package>> from uid xxx not allowed to perform READ_CLIPBOARD when calling setPrimaryClip


I've been getting reports of the following crash

SecurityException: <<other_package>> from uid xxx not allowed to perform READ_CLIPBOARD

The crash occurred when a user clicks on a button that copies a text into clipboard as shown below.

val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("label", shareUrl)
clipboard.setPrimaryClip(clip)

So my app doesn't try to read anything from clipboard. Really confusing why this error could happen.

Does anyone know how this crash happen and how can I fix this?

Additional Information

This crash only happens in Android 9 and Android 10 and not happens easily (only 6 users from 200k monthly active users)

I only saw two <<other_package>> in Crashlytics (one is a Bank app and another is a Music app).

I tried to read the source code of ClipboardService.java and AppOpsManager.java and found that the crash might came from noteOp in AppOpsManager.

Here is the stack trace of the crash:

Fatal Exception: java.lang.SecurityException: <<other_package>> from uid xxx not allowed to perform READ_CLIPBOARD
       at android.os.Parcel.createException(Parcel.java:2087)
       at android.os.Parcel.readException(Parcel.java:2055)
       at android.os.Parcel.readException(Parcel.java:2003)
       at android.content.IClipboard$Stub$Proxy.setPrimaryClip(IClipboard.java:293)
       at android.content.ClipboardManager.setPrimaryClip(ClipboardManager.java:106)
       at my.package.MyClass.copyToClipboard(MyClass.java:63)
       at android.view.View.performClick(View.java:7375)
       at android.view.View.performClickInternal(View.java:7336)
       at android.view.View.access$3900(View.java:822)
       at android.view.View$PerformClick.run(View.java:28214)
       at android.os.Handler.handleCallback(Handler.java:883)
       at android.os.Handler.dispatchMessage(Handler.java:100)
       at android.os.Looper.loop(Looper.java:238)
       at android.app.ActivityThread.main(ActivityThread.java:7829)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:986)

Caused by android.os.RemoteException: Remote stack trace:
    at android.app.AppOpsManager.noteOp(AppOpsManager.java:2568)
    at com.android.server.clipboard.ClipboardService.clipboardAccessAllowed(ClipboardService.java:933)
    at com.android.server.clipboard.ClipboardService.setPrimaryClipInternal(ClipboardService.java:775)
    at com.android.server.clipboard.ClipboardService.setPrimaryClipInternal(ClipboardService.java:710)
    at com.android.server.clipboard.ClipboardService$ClipboardImpl.setPrimaryClip(ClipboardService.java:358)


Solution

  • I can finally reproduce the crash in Android 9. This is what I found...

    Root cause: When foreground app calls setPrimaryClipboard, the ClipboardService will broadcast the event to PrimaryClipChangedListener (if any) and will also make a call to AppOpsManager.noteOp to inform about READ_CLIPBOARD operation.

    If the listener is not allowed to READ_CLIPBOARD (user can disallow by using adb shell command: cmd appops set <package> READ_CLIPBOARD deny), AppOpsManager.noteOp will throw a SecurityException and will crash the foreground app.

    Now I'm sure that my code doesn't do anything wrong, but unfortunately I think I have to put try/catch around setPrimaryClip