Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Changes from 6 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5d197bd
SecurityException: Permission Denial
hamdikahloun Sep 20, 2020
01f2153
SecurityException: Permission Denial
hamdikahloun Sep 20, 2020
9e4c909
Update PlatformPlugin.java
hamdikahloun Sep 20, 2020
795da8a
Update PlatformPlugin.java
hamdikahloun Sep 20, 2020
dce34fd
Merge branch 'master' into hamdikahloun-patch-SecurityException
hamdikahloun Sep 20, 2020
9443f65
Update PlatformPlugin.java
hamdikahloun Sep 20, 2020
8046589
Update PlatformPlugin.java
hamdikahloun Sep 28, 2020
6b74aa6
Update PlatformPlugin.java
hamdikahloun Sep 28, 2020
f1326fb
Update PlatformPlugin.java
hamdikahloun Sep 28, 2020
3a1fb98
TAG & Log
hamdikahloun Sep 29, 2020
8187b1a
SecurityException for Build Tools 29 and below
hamdikahloun Sep 29, 2020
1a28095
Add Test Uri ClipData
hamdikahloun Sep 29, 2020
ccd25ef
Add Test for Null Uri ClipData
hamdikahloun Sep 29, 2020
8f5417c
Update PlatformPluginTest.java
hamdikahloun Sep 29, 2020
d9ea4ae
Update PlatformPluginTest.java
hamdikahloun Sep 29, 2020
9668074
Update PlatformPluginTest.java
hamdikahloun Sep 29, 2020
b51174b
Update PlatformPluginTest.java
hamdikahloun Sep 29, 2020
7d5a882
Update PlatformPluginTest.java
hamdikahloun Sep 29, 2020
075248d
Update PlatformPluginTest.java
hamdikahloun Sep 29, 2020
a67c6be
Update PlatformPluginTest.java
hamdikahloun Sep 29, 2020
e57ea8a
Update PlatformPluginTest.java
hamdikahloun Sep 29, 2020
ccc1bb3
Update PlatformPluginTest.java
hamdikahloun Sep 30, 2020
051e3bd
Update PlatformPluginTest.java
hamdikahloun Sep 30, 2020
c87294f
platformPlugin_getClipboardData: assertEquals Test
hamdikahloun Oct 8, 2020
00b6be8
Update PlatformPluginTest.java
hamdikahloun Oct 8, 2020
8d04b8c
Update PlatformPluginTest.java
hamdikahloun Oct 8, 2020
5ebfcf4
Update shell/platform/android/io/flutter/plugin/platform/PlatformPlug…
dnfield Oct 8, 2020
06ca28b
Update PlatformPlugin.java
hamdikahloun Oct 8, 2020
e06d03b
Merge remote-tracking branch 'upstream/master' into hamdikahloun-patc…
hamdikahloun Oct 8, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@

package io.flutter.plugin.platform;

import static android.content.ClipDescription.MIMETYPE_TEXT_PLAIN;

import android.app.Activity;
import android.app.ActivityManager.TaskDescription;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Build;
Expand Down Expand Up @@ -283,8 +286,12 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for

if (!clipboard.hasPrimaryClip()) return null;

ClipDescription clipDescription = clipboard.getPrimaryClipDescription();
if (clipDescription == null || !clipDescription.hasMimeType(MIMETYPE_TEXT_PLAIN)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@hamdikahloun hamdikahloun Sep 21, 2020

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@dnfield i think it's SecurityException in clip.getItemAt(0).coerceToText(activity) If getUri() is non-null and try to retrieve its data as a text stream from its content provider :

W/ClipData(29672): java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/file/125 from pid=29672, uid=10307 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()

So that's why i have to check if it's a MIMETYPE_TEXT_PLAIN:

ClipDescription clipDescription = clipboard.getPrimaryClipDescription();
    if (clipDescription == null || !clipDescription.hasMimeType(MIMETYPE_TEXT_PLAIN)) {
      return null;
    }

Maybe we should use checkUriPermission :

 ClipData clip = clipboard.getPrimaryClip();
    if (format == null || format == PlatformChannel.ClipboardContentFormat.PLAIN_TEXT) {
      Uri uri = clip.getItemAt(0).getUri();
      
      if (uri != null && uri.getEncodedPath() != null) {      
     int uriPermission =
    	activity.checkUriPermission(
    		    	                uri,
    		    	                android.os.Process.myPid(),
    		    	                android.os.Process.myUid(),
    		    	                Intent.FLAG_GRANT_READ_URI_PERMISSION);
    	  
     if (uriPermission == PackageManager.PERMISSION_DENIED) return null;
    }
      
      return clip.getItemAt(0).coerceToText(activity);
    }

Or :

File file = new File(uri.getEncodedPath());
if (!file.canRead()) return null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think checking for the permission is pretty reasonable. I would say that if the app doesn't have permission, it would probably be helpful to have a debug log in there informing the developer that they should add it to their application to support this kind of copying and pasting.

What if the clip is the intent type?

@hamdikahloun hamdikahloun Sep 21, 2020

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

What if the clip is the intent type?

The intent will be converted to String without any problem

Other Option :

 String scheme = uri.getScheme();
   if (SCHEME_CONTENT.equals(scheme)
     || SCHEME_ANDROID_RESOURCE.equals(scheme)
     || SCHEME_FILE.equals(scheme)) {
           return null;
                }

Or

Try...Catch for SecurityException with debug log

coerceToText Source Code
  public CharSequence coerceToText(Context context) {
            // If this Item has an explicit textual value, simply return that.
            CharSequence text = getText();
            if (text != null) {
                return text;
            }

            // If this Item has a URI value, try using that.
            Uri uri = getUri();
            if (uri != null) {
                // First see if the URI can be opened as a plain text stream
                // (of any sub-type).  If so, this is the best textual
                // representation for it.
                final ContentResolver resolver = context.getContentResolver();
                AssetFileDescriptor descr = null;
                FileInputStream stream = null;
                InputStreamReader reader = null;
                try {
                    try {
                        // Ask for a stream of the desired type.
                        descr = resolver.openTypedAssetFileDescriptor(uri, "text/*", null);
                    } catch (SecurityException e) {
                        Log.w("ClipData", "Failure opening stream", e);
                    } catch (FileNotFoundException|RuntimeException e) {
                        // Unable to open content URI as text...  not really an
                        // error, just something to ignore.
                    }
                    if (descr != null) {
                        try {
                            stream = descr.createInputStream();
                            reader = new InputStreamReader(stream, "UTF-8");

                            // Got it...  copy the stream into a local string and return it.
                            final StringBuilder builder = new StringBuilder(128);
                            char[] buffer = new char[8192];
                            int len;
                            while ((len=reader.read(buffer)) > 0) {
                                builder.append(buffer, 0, len);
                            }
                            return builder.toString();
                        } catch (IOException e) {
                            // Something bad has happened.
                            Log.w("ClipData", "Failure loading text", e);
                            return e.toString();
                        }
                    }
                } finally {
                    IoUtils.closeQuietly(descr);
                    IoUtils.closeQuietly(stream);
                    IoUtils.closeQuietly(reader);
                }

                // If we couldn't open the URI as a stream, use the URI itself as a textual
                // representation (but not for "content", "android.resource" or "file" schemes).
                final String scheme = uri.getScheme();
                if (SCHEME_CONTENT.equals(scheme)
                        || SCHEME_ANDROID_RESOURCE.equals(scheme)
                        || SCHEME_FILE.equals(scheme)) {
                    return "";
                }
                return uri.toString();
            }

            // Finally, if all we have is an Intent, then we can just turn that
            // into text.  Not the most user-friendly thing, but it's something.
            Intent intent = getIntent();
            if (intent != null) {
                return intent.toUri(Intent.URI_INTENT_SCHEME);
            }

            // Shouldn't get here, but just in case...
            return "";
        }
  • URI
    If ClipData.Item is a URI (getUri() is not null), coerceToText() tries to use it as a content URI:
    If the URI is a content URI and the provider can return a text stream, coerceToText() returns a text stream.
    If the URI is a content URI but the provider does not offer a text stream, coerceToText() returns a representation of the URI. The representation is the same as that returned by Uri.toString().
    If the URI is not a content URI, coerceToText() returns a representation of the URI. The representation is the same as that returned by Uri.toString().

  • Intent
    If ClipData.Item is an Intent (getIntent() is not null), coerceToText() converts it to an Intent URI and returns it. The representation is the same as that returned by Intent.toUri(URI_INTENT_SCHEME).

Source : CoerceToText

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

return null;
}

ClipData clip = clipboard.getPrimaryClip();
if (clip == null) return null;

if (format == null || format == PlatformChannel.ClipboardContentFormat.PLAIN_TEXT) {
return clip.getItemAt(0).coerceToText(activity);
Expand Down