Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 0a005c4

Browse files
authored
Fix new lint from android 14 upgrade, and remove it from the baseline (#47817)
See #47609 (comment) for context. It isn't clear to me what the file descriptor here is actually doing, so I'm not actually too sure about this fix. Can it just be deleted? Update: it seems to me that the motivation here is that `ClipData.Item.coerceToText` [consumes a `SecurityException` here](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/content/ClipData.java;l=411;bpv=0;bpt=1), with just a log line that isn't particularly descriptive. And basically we want to run into that same exception so we can [provide a more helpful log line](https://github.com/flutter/engine/blob/00db306f6f7b42b5664d604f33a0de426edf7109/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java#L525), so we do the same thing that the underlying `coerceToText` method does? [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 60b9639 commit 0a005c4

File tree

3 files changed

+42
-14
lines changed

3 files changed

+42
-14
lines changed

shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import android.content.ClipDescription;
1212
import android.content.ClipboardManager;
1313
import android.content.Context;
14+
import android.content.res.AssetFileDescriptor;
1415
import android.os.Build;
1516
import android.view.HapticFeedbackConstants;
1617
import android.view.SoundEffectConstants;
@@ -25,6 +26,7 @@
2526
import io.flutter.Log;
2627
import io.flutter.embedding.engine.systemchannels.PlatformChannel;
2728
import java.io.FileNotFoundException;
29+
import java.io.IOException;
2830
import java.util.List;
2931

3032
/** Android implementation of the platform plugin. */
@@ -512,14 +514,21 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for
512514

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

517+
CharSequence charSequence = null;
515518
try {
516519
ClipData clip = clipboard.getPrimaryClip();
517520
if (clip == null) return null;
518521
if (format == null || format == PlatformChannel.ClipboardContentFormat.PLAIN_TEXT) {
519522
ClipData.Item item = clip.getItemAt(0);
523+
AssetFileDescriptor assetFileDescriptor = null;
520524
if (item.getUri() != null)
521-
activity.getContentResolver().openTypedAssetFileDescriptor(item.getUri(), "text/*", null);
522-
return item.coerceToText(activity);
525+
assetFileDescriptor =
526+
activity
527+
.getContentResolver()
528+
.openTypedAssetFileDescriptor(item.getUri(), "text/*", null);
529+
charSequence = item.coerceToText(activity);
530+
if (assetFileDescriptor != null) assetFileDescriptor.close();
531+
return charSequence;
523532
}
524533
} catch (SecurityException e) {
525534
Log.w(
@@ -531,6 +540,9 @@ private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat for
531540
return null;
532541
} catch (FileNotFoundException e) {
533542
return null;
543+
} catch (IOException e) {
544+
Log.w(TAG, "Failed to close AssetFileDescriptor while accessing clipboard data.", e);
545+
return charSequence;
534546
}
535547

536548
return null;

shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
import static org.junit.Assert.assertNull;
1212
import static org.junit.Assert.assertTrue;
1313
import static org.mockito.Mockito.anyBoolean;
14+
import static org.mockito.Mockito.anyString;
15+
import static org.mockito.Mockito.doReturn;
16+
import static org.mockito.Mockito.doThrow;
17+
import static org.mockito.Mockito.eq;
1418
import static org.mockito.Mockito.mock;
1519
import static org.mockito.Mockito.never;
1620
import static org.mockito.Mockito.spy;
@@ -20,9 +24,11 @@
2024

2125
import android.app.Activity;
2226
import android.content.ClipData;
27+
import android.content.ClipDescription;
2328
import android.content.ClipboardManager;
2429
import android.content.ContentResolver;
2530
import android.content.Context;
31+
import android.content.res.AssetFileDescriptor;
2632
import android.net.Uri;
2733
import android.os.Build;
2834
import android.view.View;
@@ -84,18 +90,39 @@ public void platformPlugin_getClipboardData() throws IOException {
8490
PlatformChannel fakePlatformChannel = mock(PlatformChannel.class);
8591
PlatformPlugin platformPlugin = new PlatformPlugin(fakeActivity, fakePlatformChannel);
8692

93+
// Successfully get the contents of the primary clip when they contain text.
8794
ClipboardContentFormat clipboardFormat = ClipboardContentFormat.PLAIN_TEXT;
8895
assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat));
8996
ClipData clip = ClipData.newPlainText("label", "Text");
9097
clipboardManager.setPrimaryClip(clip);
9198
assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat));
9299

93-
ContentResolver contentResolver = ctx.getContentResolver();
100+
// Return null when the primary clip contains non-text media.
101+
ContentResolver contentResolver = spy(ctx.getContentResolver());
94102
when(fakeActivity.getContentResolver()).thenReturn(contentResolver);
95103
Uri uri = Uri.parse("content://media/external_primary/images/media/");
96104
clip = ClipData.newUri(contentResolver, "URI", uri);
97105
clipboardManager.setPrimaryClip(clip);
98106
assertNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat));
107+
108+
// Still return text when the AssetFileDescriptor throws an IOException.
109+
when(fakeActivity.getContentResolver()).thenReturn(contentResolver);
110+
ClipDescription clipDescription =
111+
new ClipDescription(
112+
"label",
113+
new String[] {
114+
ClipDescription.MIMETYPE_TEXT_PLAIN, ClipDescription.MIMETYPE_TEXT_URILIST
115+
});
116+
ClipData.Item clipDataItem = new ClipData.Item("Text", null, uri);
117+
ClipData clipData = new ClipData(clipDescription, clipDataItem);
118+
clipboardManager.setPrimaryClip(clipData);
119+
AssetFileDescriptor fakeAssetFileDescriptor = mock(AssetFileDescriptor.class);
120+
doReturn(fakeAssetFileDescriptor)
121+
.when(contentResolver)
122+
.openTypedAssetFileDescriptor(eq(uri), anyString(), eq(null));
123+
doThrow(new IOException()).when(fakeAssetFileDescriptor).close();
124+
assertNotNull(platformPlugin.mPlatformMessageHandler.getClipboardData(clipboardFormat));
125+
verify(fakeAssetFileDescriptor).close();
99126
}
100127

101128
@SuppressWarnings("deprecation")

tools/android_lint/baseline.xml

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,6 @@
6767
column="82"/>
6868
</issue>
6969

70-
<issue
71-
id="Recycle"
72-
message="This `AssetFileDescriptor` should be freed up after use with `#close()`"
73-
errorLine1=" activity.getContentResolver().openTypedAssetFileDescriptor(item.getUri(), &quot;text/*&quot;, null);"
74-
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
75-
<location
76-
file="../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java"
77-
line="522"
78-
column="41"/>
79-
</issue>
80-
8170
<issue
8271
id="ClickableViewAccessibility"
8372
message="Custom view `FlutterView` overrides `onTouchEvent` but not `performClick`"

0 commit comments

Comments
 (0)