diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java
index 41ffa23853c7c..c3c14dff25213 100644
--- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java
+++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java
@@ -11,6 +11,7 @@
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.Context;
+import android.content.res.AssetFileDescriptor;
import android.os.Build;
import android.view.HapticFeedbackConstants;
import android.view.SoundEffectConstants;
@@ -25,6 +26,7 @@
import io.flutter.Log;
import io.flutter.embedding.engine.systemchannels.PlatformChannel;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.util.List;
/** Android implementation of the platform plugin. */
@@ -512,14 +514,21 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for
if (!clipboard.hasPrimaryClip()) return null;
+ CharSequence charSequence = null;
try {
ClipData clip = clipboard.getPrimaryClip();
if (clip == null) return null;
if (format == null || format == PlatformChannel.ClipboardContentFormat.PLAIN_TEXT) {
ClipData.Item item = clip.getItemAt(0);
+ AssetFileDescriptor assetFileDescriptor = null;
if (item.getUri() != null)
- activity.getContentResolver().openTypedAssetFileDescriptor(item.getUri(), "text/*", null);
- return item.coerceToText(activity);
+ assetFileDescriptor =
+ activity
+ .getContentResolver()
+ .openTypedAssetFileDescriptor(item.getUri(), "text/*", null);
+ charSequence = item.coerceToText(activity);
+ if (assetFileDescriptor != null) assetFileDescriptor.close();
+ return charSequence;
}
} catch (SecurityException e) {
Log.w(
@@ -531,6 +540,9 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for
return null;
} catch (FileNotFoundException e) {
return null;
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to close AssetFileDescriptor while accessing clipboard data.", e);
+ return charSequence;
}
return null;
diff --git a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java
index 23b7fbf5b6a98..51e66c91db30b 100644
--- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java
+++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java
@@ -11,6 +11,10 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -20,9 +24,11 @@
import android.app.Activity;
import android.content.ClipData;
+import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.res.AssetFileDescriptor;
import android.net.Uri;
import android.os.Build;
import android.view.View;
@@ -84,18 +90,39 @@ public void platformPlugin_getClipboardData() throws IOException {
PlatformChannel fakePlatformChannel = mock(PlatformChannel.class);
PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel);
+ // Successfully get the contents of the primary clip when they contain text.
ClipboardContentFormat clipboardFormat = ClipboardContentFormat.PLAIN_TEXT;
assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat));
ClipData clip = ClipData.newPlainText("label", "Text");
clipboardManager.setPrimaryClip(clip);
assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat));
- ContentResolver contentResolver = ctx.getContentResolver();
+ // Return null when the primary clip contains non-text media.
+ ContentResolver contentResolver = spy(ctx.getContentResolver());
when(fakeActivity.getContentResolver()).thenReturn(contentResolver);
Uri uri = Uri.parse("content://media/external_primary/images/media/");
clip = ClipData.newUri(contentResolver, "URI", uri);
clipboardManager.setPrimaryClip(clip);
assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat));
+
+ // Still return text when the AssetFileDescriptor throws an IOException.
+ when(fakeActivity.getContentResolver()).thenReturn(contentResolver);
+ ClipDescription clipDescription =
+ new ClipDescription(
+ "label",
+ new String[] {
+ ClipDescription.MIMETYPE_TEXT_PLAIN, ClipDescription.MIMETYPE_TEXT_URILIST
+ });
+ ClipData.Item clipDataItem = new ClipData.Item("Text", null, uri);
+ ClipData clipData = new ClipData(clipDescription, clipDataItem);
+ clipboardManager.setPrimaryClip(clipData);
+ AssetFileDescriptor fakeAssetFileDescriptor = mock(AssetFileDescriptor.class);
+ doReturn(fakeAssetFileDescriptor)
+ .when(contentResolver)
+ .openTypedAssetFileDescriptor(eq(uri), anyString(), eq(null));
+ doThrow(new IOException()).when(fakeAssetFileDescriptor).close();
+ assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat));
+ verify(fakeAssetFileDescriptor).close();
}
@SuppressWarnings("deprecation")
diff --git a/tools/android_lint/baseline.xml b/tools/android_lint/baseline.xml
index b540917bd520e..2fccb83d76cf3 100644
--- a/tools/android_lint/baseline.xml
+++ b/tools/android_lint/baseline.xml
@@ -67,17 +67,6 @@
column="82"/>
-
-
-
-