Skip to content

[super_clipboard] App crashes on Android after copying an image and restart the app #435

Closed
singerdmx/flutter-quill
#2230
@EchoEllet

Description

This is an issue when using super_clipboard however the native platform code is in super_native_extensions.

Steps to reproduce:

  1. Run the app on Android, copy an image from another app while the app is running.
  2. Paste it into the app, it should succeed without any issues.
  3. Refrain from copying anything to the clipboard after that.
  4. Close the app completely and then start it again (using flutter run), you might get a crash.
  5. If you didn't get any crashes, try to paste the same image from step 1.
Crash Log
Shutting down VM
E/flutter ( 7331): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(super_native_extensions_error, "JNI: Java exception was thrown", otherError, null)
E/flutter ( 7331): #0      NativeMethodChannel.invokeMethod (package:irondash_message_channel/src/method_channel.dart:45:7)
E/flutter ( 7331): <asynchronous suspension>
E/flutter ( 7331): #1      ReaderManagerImpl.getItemInfo (package:super_native_extensions/src/native/reader_manager.dart:242:18)
E/flutter ( 7331): <asynchronous suspension>
E/flutter ( 7331): #2      SystemClipboard.read (package:super_clipboard/src/system_clipboard.dart:38:22)
E/flutter ( 7331): <asynchronous suspension>
E/flutter ( 7331): #3      SuperClipboardService.hasClipboardContent (package:flutter_quill_extensions/src/editor_toolbar_controller_shared/clipboard/super_clipboard_service.dart:137:20)
E/flutter ( 7331): <asynchronous suspension>
E/flutter ( 7331): #4      ClipboardMonitor._update (package:flutter_quill/src/toolbar/buttons/clipboard_button.dart:32:9)
E/flutter ( 7331): <asynchronous suspension>
E/flutter ( 7331): 
E/AndroidRuntime( 7331): FATAL EXCEPTION: main
E/AndroidRuntime( 7331): Process: com.example.example, PID: 7331
E/AndroidRuntime( 7331): java.lang.SecurityException: Permission Denial: opening provider org.chromium.chrome.browser.util.ChromeFileProvider from ProcessRecord{2832f48 7331:com.example.example/u0a191} (pid=7331, uid=10191) that is not exported from UID 10134
E/AndroidRuntime( 7331): 	at android.os.Parcel.createExceptionOrNull(Parcel.java:3057)
E/AndroidRuntime( 7331): 	at android.os.Parcel.createException(Parcel.java:3041)
E/AndroidRuntime( 7331): 	at android.os.Parcel.readException(Parcel.java:3024)
E/AndroidRuntime( 7331): 	at android.os.Parcel.readException(Parcel.java:2966)
E/AndroidRuntime( 7331): 	at android.app.IActivityManager$Stub$Proxy.getContentProvider(IActivityManager.java:5906)
E/AndroidRuntime( 7331): 	at android.app.ActivityThread.acquireProvider(ActivityThread.java:7310)
E/AndroidRuntime( 7331): 	at android.app.ContextImpl$ApplicationContentResolver.acquireProvider(ContextImpl.java:3649)
E/AndroidRuntime( 7331): 	at android.content.ContentResolver.acquireProvider(ContentResolver.java:2493)
E/AndroidRuntime( 7331): 	at android.content.ContentResolver.getStreamTypes(ContentResolver.java:1066)
E/AndroidRuntime( 7331): 	at com.superlist.super_native_extensions.ClipDataHelper.getFormats(ClipDataHelper.java:92)
E/AndroidRuntime( 7331): 	at com.superlist.super_native_extensions.ClipDataHelper.getFormats(ClipDataHelper.java:41)
E/AndroidRuntime( 7331): 	at android.os.MessageQueue.nativePollOnce(Native Method)
E/AndroidRuntime( 7331): 	at android.os.MessageQueue.next(MessageQueue.java:335)
E/AndroidRuntime( 7331): 	at android.os.Looper.loopOnce(Looper.java:162)
E/AndroidRuntime( 7331): 	at android.os.Looper.loop(Looper.java:294)
E/AndroidRuntime( 7331): 	at android.app.ActivityThread.main(ActivityThread.java:8177)
E/AndroidRuntime( 7331): 	at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime( 7331): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
E/AndroidRuntime( 7331): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
E/AndroidRuntime( 7331): Caused by: android.os.RemoteException: Remote stack trace:
E/AndroidRuntime( 7331): 	at com.android.server.am.ContentProviderHelper.checkAssociationAndPermissionLocked(ContentProviderHelper.java:691)
E/AndroidRuntime( 7331): 	at com.android.server.am.ContentProviderHelper.getContentProviderImpl(ContentProviderHelper.java:287)
E/AndroidRuntime( 7331): 	at com.android.server.am.ContentProviderHelper.getContentProvider(ContentProviderHelper.java:144)
E/AndroidRuntime( 7331): 	at com.android.server.am.ActivityManagerService.getContentProvider(ActivityManagerService.java:6713)
E/AndroidRuntime( 7331): 	at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2761)
Video

Android Emulator API 34.

SuperClipboardAndroidCrash.mov
Similar but not related issue

This issue is not related and doesn't use super_clipbaord however a similar issue will occur with the same steps to reproduce.

It uses our custom implementation in Flutter Quill #2230 using Kotlin (for Android) directly using the Flutter method channel without Rust.

Screen.Recording.2024-09-18.at.4.53.08.PM.mov

The difference is that the app doesn't crash:

Unhandled Exception: PlatformException(COULD_NOT_DECODE_IMAGE, Could not decode bitmap from Uri: Permission Denial: opening provider org.chromium.chrome.browser.util.ChromeFileProvider from ProcessRecord{b3d4d8e 14685:dev.flutterquill.quill_native_bridge_example/u0a191} (pid=14685, uid=10191) that is not exported from UID 10146, java.lang.SecurityException: Permission Denial: opening provider org.chromium.chrome.browser.util.ChromeFileProvider from ProcessRecord{b3d4d8e 14685:dev.flutterquill.quill_native_bridge_example/u0a191} (pid=14685, uid=10191) that is not exported from UID 10146, null)
E/flutter (14685): #0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:648:7)
E/flutter (14685): #1      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:334:18)
E/flutter (14685): <asynchronous suspension>
E/flutter (14685): #2      MethodChannelQuillNativeBridge.getClipboardImage (package:quill_native_bridge/src/quill_native_bridge_method_channel.dart:87:24)
E/flutter (14685): <asynchronous suspension>
E/flutter (14685): #3      Buttons.build.<anonymous closure> (package:quill_native_bridge_example/main.dart:150:32)
E/flutter (14685): <asynchronous suspension>
E/flutter (14685): 

It's handled in a way so it returns null and throws an exception instead though I'm not entirely sure about the exact issue with super_clipbaord implementation.

You might noticed that both quill_native_bridge (QuillNativeBridgePlugin) and super_clipbaord have java.lang.SecurityException: Permission Denial in the console, which confirms that it's a security issue related to the lifecycle of the app.

Sometimes it can be:

java.io.FileNotFoundException: No content provider: content://com.android.chrome.FileProvider/images/screenshot/17266705501256549663602733807552.gif
W/System.err(17433): 	at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:2029)
W/System.err(17433): 	at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1858)
W/System.err(17433): 	at android.content.ContentResolver.openInputStream(ContentResolver.java:1528)

Or:

java.lang.SecurityException: Permission Denial: opening provider org.chromium.chrome.browser.util.ChromeFileProvider from ProcessRecord{d1d20d7 17742:dev.flutterquill.quill_native_bridge_example/u0a191} (pid=17742, uid=10191) that is not exported from UID 10146
W/System.err(17742): 	at android.os.Parcel.createExceptionOrNull(Parcel.java:3057)
W/System.err(17742): 	at android.os.Parcel.createException(Parcel.java:3041)
W/System.err(17742): 	at android.os.Parcel.readException(Parcel.java:3024)
W/System.err(17742): 	at android.os.Parcel.readException(Parcel.java:2966)
W/System.err(17742): 	at android.app.IActivityManager$Stub$Proxy.getContentProvider(IActivityManager.java:5906)
W/System.err(17742): 	at android.app.ActivityThread.acquireProvider(ActivityThread.java:7310)
W/System.err(17742): 	at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:3668)
W/System.err(17742): 	at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:2542)
W/System.err(17742): 	at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:2027)
W/System.err(17742): 	at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1858)
W/System.err(17742): 	at android.content.ContentResolver.openInputStream(ContentResolver.java:1528)
W/System.err(17742): 	at dev.flutterquill.quill_native_bridge.clipboard.ClipboardImageHandler.canRead(ClipboardImageHandler.kt:86)
W/System.err(17742): 	at dev.flutterquill.quill_native_bridge.clipboard.ClipboardImageHandler.getClipboardImage(ClipboardImageHandler.kt:114)
W/System.err(17742): 	at dev.flutterquill.quill_native_bridge.QuillNativeBridgePlugin.onMethodCall(QuillNativeBridgePlugin.kt:169)
W/System.err(17742): 	at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:267)
W/System.err(17742): 	at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:292)
W/System.err(17742): 	at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:319)
W/System.err(17742): 	at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12)
W/System.err(17742): 	at android.os.Handler.handleCallback(Handler.java:958)
W/System.err(17742): 	at android.os.Handler.dispatchMessage(Handler.java:99)
W/System.err(17742): 	at android.os.Looper.loopOnce(Looper.java:205)
W/System.err(17742): 	at android.os.Looper.loop(Looper.java:294)
W/System.err(17742): 	at android.app.ActivityThread.main(ActivityThread.java:8177)
W/System.err(17742): 	at java.lang.reflect.Method.invoke(Native Method)
W/System.err(17742): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
W/System.err(17742): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
W/System.err(17742): Caused by: android.os.RemoteException: Remote stack trace:
W/System.err(17742): 	at com.android.server.am.ContentProviderHelper.checkAssociationAndPermissionLocked(ContentProviderHelper.java:691)
W/System.err(17742): 	at com.android.server.am.ContentProviderHelper.getContentProviderImpl(ContentProviderHelper.java:287)
W/System.err(17742): 	at com.android.server.am.ContentProviderHelper.getContentProvider(ContentProviderHelper.java:144)
W/System.err(17742): 	at com.android.server.am.ActivityManagerService.getContentProvider(ActivityManagerService.java:6713)
W/System.err(17742): 	at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2761)

The only option I had was to write this image to a temporary location or internal storage, then once paste it again on app restart (when not having access to it), then get access to the already saved image, which seems like a workaround to me, there are similar issues on Android that need to solved manually and can be error prone such as process death. So I tried super_clipboard to see how it's handled, and the impl we have doesn't have the exact same exception details as super_clipboard, it seems like a similar unrelated issue.

Hint: The image URI that is given by the android.content.ClipboardManager gives the app limited access, and can be restricted to its lifecycle, once the lifecycle is destroyed, the app no longer has access to the android.net.Uri.

The super_clipboard plugin could check if it has access before processing further, avoid resulting in a crash, and return null (false for the canProvide or throw a dart platform exception) or save the image to somewhere and get access to the image later on app restart and return the image.

let me know If more details are needed or unable to reproduce the issue.

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions