From 7c342bbebdff26980021dda491aa5917a864a2e7 Mon Sep 17 00:00:00 2001 From: Gray Mackall Date: Tue, 26 Nov 2024 13:37:50 -0800 Subject: [PATCH 1/6] largely copy other pr --- .../plugins/imagepicker/FileUtils.java | 46 +++++++++++++-- .../plugins/imagepicker/FileUtilTest.java | 59 +++++++++++++++++++ 2 files changed, 100 insertions(+), 5 deletions(-) diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java index 8afed83aaef0..4dd196a33294 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java @@ -29,6 +29,10 @@ import android.net.Uri; import android.provider.MediaStore; import android.webkit.MimeTypeMap; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import io.flutter.Log; import java.io.File; import java.io.FileOutputStream; @@ -42,6 +46,11 @@ class FileUtils { * Copies the file from the given content URI to a temporary directory, retaining the original * file name if possible. * + *

If the filename contains path indirection or separators (.. or /), the end file name will be + * the segment after the final separator, with indirection replaced by underscores. + * E.g. "example/../..file.png" -> "_file.png". + * See: Improperly trusting ContentProvider-provided filename. + * *

Each file is placed in its own directory to avoid conflicts according to the following * scheme: {cacheDir}/{randomUuid}/{fileName} * @@ -69,10 +78,11 @@ String getPathFromUri(final Context context, final Uri uri) { } else if (extension != null) { fileName = getBaseName(fileName) + extension; } - File file = new File(targetDirectory, fileName); - try (OutputStream outputStream = new FileOutputStream(file)) { + String filePath = new File(targetDirectory, fileName).getPath(); + File outputFile = saferOpenFile(filePath, targetDirectory.getCanonicalPath()); + try (OutputStream outputStream = new FileOutputStream(outputFile)) { copy(inputStream, outputStream); - return file.getPath(); + return outputFile.getPath(); } } catch (IOException e) { // If closing the output stream fails, we cannot be sure that the @@ -110,14 +120,40 @@ private static String getImageExtension(Context context, Uri uriImage) { return null; } - return "." + extension; + return "." + sanitizeFilename(extension); + } + + // From https://developer.android.com/privacy-and-security/risks/untrustworthy-contentprovider-provided-filename#sanitize-provided-filenames. + protected static @Nullable String sanitizeFilename(@Nullable String displayName) { + if (displayName == null) { + return null; + } + + String[] badCharacters = new String[] { "..", "/" }; + String[] segments = displayName.split("/"); + String fileName = segments[segments.length - 1]; + for (String suspString : badCharacters) { + fileName = fileName.replace(suspString, "_"); + } + return fileName; + } + + // From https://developer.android.com/privacy-and-security/risks/path-traversal#path-traversal-mitigations. + protected static @NonNull File saferOpenFile(@NonNull String path, @NonNull String expectedDir) throws IllegalArgumentException, IOException { + File f = new File(path); + String canonicalPath = f.getCanonicalPath(); + if (!canonicalPath.startsWith(expectedDir)) { + throw new IllegalArgumentException("Trying to open path outside of the expected directory. File: " + f.getCanonicalPath() + " was expected to be within directory: " + expectedDir + "."); + } + return f; } /** @return name of the image provided by ContentResolver; this may be null. */ private static String getImageName(Context context, Uri uriImage) { try (Cursor cursor = queryImageName(context, uriImage)) { if (cursor == null || !cursor.moveToFirst() || cursor.getColumnCount() < 1) return null; - return cursor.getString(0); + String unsanitizedImageName = cursor.getString(0); + return sanitizeFilename(unsanitizedImageName); } } diff --git a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java index 620bac74a17b..e60e6406d44f 100644 --- a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java +++ b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java @@ -6,6 +6,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -129,6 +130,17 @@ public void FileUtil_getImageName_mismatchedType() throws IOException { assertTrue(path.endsWith("c.d.webp")); } + @Test + public void getPathFromUri_sanitizesPathIndirection() { + Uri uri = Uri.parse(MockMaliciousContentProvider.PNG_URI); + Robolectric.buildContentProvider(MockMaliciousContentProvider.class).create("dummy"); + shadowContentResolver.registerInputStream( + uri, new ByteArrayInputStream("fileStream".getBytes(UTF_8))); + String path = fileUtils.getPathFromUri(context, uri); + assertNotNull(path); + assertTrue(path.endsWith("_bar.png")); + } + @Test public void FileUtil_getImageName_unknownType() throws IOException { Uri uri = MockContentProvider.UNKNOWN_URI; @@ -193,4 +205,51 @@ public int update( return 0; } } + + // Mocks a malicious content provider attempting to use path indirection to modify files outside + // of the intended directory. + // See https://developer.android.com/privacy-and-security/risks/untrustworthy-contentprovider-provided-filename#don%27t-trust-user-input. + private static class MockMaliciousContentProvider extends ContentProvider { + public static String PNG_URI = "content://dummy/a.png"; + + @Override + public boolean onCreate() { + return true; + } + + @Nullable + @Override + public Cursor query( + @NonNull Uri uri, + @Nullable String[] projection, + @Nullable String selection, + @Nullable String[] selectionArgs, + @Nullable String sortOrder) { + MatrixCursor cursor = new MatrixCursor(new String[] {MediaStore.MediaColumns.DISPLAY_NAME}); + cursor.addRow(new Object[] {"foo/../..bar.png"}); + return cursor; + } + + @Nullable + @Override + public String getType(@NonNull Uri uri) { + return "image/png"; + } + + @Nullable + @Override + public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { + return null; + } + + @Override + public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { + return 0; + } + + @Override + public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { + return 0; + } + } } From 4767c3c288aa8c1c934b51d999440506b7a33469 Mon Sep 17 00:00:00 2001 From: Gray Mackall Date: Tue, 26 Nov 2024 13:45:54 -0800 Subject: [PATCH 2/6] pubspec+changelog --- packages/image_picker/image_picker_android/CHANGELOG.md | 4 ++++ packages/image_picker/image_picker_android/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 7616f8f6dce4..ada4c073ddd6 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.12+18 + +* Fixes a security issue related to improperly trusting filenames provided by a `ContentProvider`. + ## 0.8.12+17 * Bumps androidx.annotation:annotation from 1.8.2 to 1.9.0. diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index 605dc0c1585a..b3f294e716ae 100755 --- a/packages/image_picker/image_picker_android/pubspec.yaml +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_android description: Android implementation of the image_picker plugin. repository: https://github.com/flutter/packages/tree/main/packages/image_picker/image_picker_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.12+17 +version: 0.8.12+18 environment: sdk: ^3.5.0 From ed5e6ce1c80ae16cc49cbd3b10d7742349130c82 Mon Sep 17 00:00:00 2001 From: Gray Mackall Date: Wed, 27 Nov 2024 13:28:30 -0800 Subject: [PATCH 3/6] let new pigeon version do its thing --- .../flutter/plugins/imagepicker/Messages.java | 177 ++++++----------- .../lib/src/messages.g.dart | 98 ++++------ .../image_picker_android/test/test_api.g.dart | 179 +++++++----------- 3 files changed, 166 insertions(+), 288 deletions(-) diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java index c11f20e228d4..e2f4a541b13e 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.4.1), do not edit directly. +// Autogenerated from Pigeon (v22.6.2), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.imagepicker; @@ -21,7 +21,11 @@ import java.lang.annotation.Target; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; /** Generated class from Pigeon. */ @@ -37,7 +41,8 @@ public static class FlutterError extends RuntimeException { /** The error details. Must be a datatype supported by the api codec. */ public final Object details; - public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) { + public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) + { super(message); this.code = code; this.details = details; @@ -56,7 +61,7 @@ protected static ArrayList wrapError(@NonNull Throwable exception) { errorList.add(exception.toString()); errorList.add(exception.getClass().getSimpleName()); errorList.add( - "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); } return errorList; } @@ -141,16 +146,10 @@ public void setLimit(@Nullable Long setterArg) { @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } GeneralOptions that = (GeneralOptions) o; - return allowMultiple.equals(that.allowMultiple) - && usePhotoPicker.equals(that.usePhotoPicker) - && Objects.equals(limit, that.limit); + return allowMultiple.equals(that.allowMultiple) && usePhotoPicker.equals(that.usePhotoPicker) && Objects.equals(limit, that.limit); } @Override @@ -217,7 +216,7 @@ ArrayList toList() { /** * Options for image selection and output. * - *

Generated class from Pigeon that represents data sent in messages. + * Generated class from Pigeon that represents data sent in messages. */ public static final class ImageSelectionOptions { /** If set, the max width that the image should be resized to fit in. */ @@ -245,7 +244,7 @@ public void setMaxHeight(@Nullable Double setterArg) { /** * The quality of the output image, from 0-100. * - *

100 indicates original quality. + * 100 indicates original quality. */ private @NonNull Long quality; @@ -265,16 +264,10 @@ public void setQuality(@NonNull Long setterArg) { @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } ImageSelectionOptions that = (ImageSelectionOptions) o; - return Objects.equals(maxWidth, that.maxWidth) - && Objects.equals(maxHeight, that.maxHeight) - && quality.equals(that.quality); + return Objects.equals(maxWidth, that.maxWidth) && Objects.equals(maxHeight, that.maxHeight) && quality.equals(that.quality); } @Override @@ -358,12 +351,8 @@ public void setImageSelectionOptions(@NonNull ImageSelectionOptions setterArg) { @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } MediaSelectionOptions that = (MediaSelectionOptions) o; return imageSelectionOptions.equals(that.imageSelectionOptions); } @@ -408,7 +397,7 @@ ArrayList toList() { /** * Options for image selection and output. * - *

Generated class from Pigeon that represents data sent in messages. + * Generated class from Pigeon that represents data sent in messages. */ public static final class VideoSelectionOptions { /** The maximum desired length for the video, in seconds. */ @@ -424,12 +413,8 @@ public void setMaxDurationSeconds(@Nullable Long setterArg) { @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } VideoSelectionOptions that = (VideoSelectionOptions) o; return Objects.equals(maxDurationSeconds, that.maxDurationSeconds); } @@ -474,7 +459,7 @@ ArrayList toList() { /** * Specification for the source of an image or video selection. * - *

Generated class from Pigeon that represents data sent in messages. + * Generated class from Pigeon that represents data sent in messages. */ public static final class SourceSpecification { private @NonNull SourceType type; @@ -505,12 +490,8 @@ public void setCamera(@Nullable SourceCamera setterArg) { @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } SourceSpecification that = (SourceSpecification) o; return type.equals(that.type) && Objects.equals(camera, that.camera); } @@ -567,9 +548,9 @@ ArrayList toList() { /** * An error that occurred during lost result retrieval. * - *

The data here maps to the `PlatformException` that will be created from it. + * The data here maps to the `PlatformException` that will be created from it. * - *

Generated class from Pigeon that represents data sent in messages. + * Generated class from Pigeon that represents data sent in messages. */ public static final class CacheRetrievalError { private @NonNull String code; @@ -600,12 +581,8 @@ public void setMessage(@Nullable String setterArg) { @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } CacheRetrievalError that = (CacheRetrievalError) o; return code.equals(that.code) && Objects.equals(message, that.message); } @@ -662,7 +639,7 @@ ArrayList toList() { /** * The result of retrieving cached results from a previous run. * - *

Generated class from Pigeon that represents data sent in messages. + * Generated class from Pigeon that represents data sent in messages. */ public static final class CacheRetrievalResult { /** The type of the retrieved data. */ @@ -709,16 +686,10 @@ public void setPaths(@NonNull List setterArg) { @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } CacheRetrievalResult that = (CacheRetrievalResult) o; - return type.equals(that.type) - && Objects.equals(error, that.error) - && paths.equals(that.paths); + return type.equals(that.type) && Objects.equals(error, that.error) && paths.equals(that.paths); } @Override @@ -790,21 +761,18 @@ private PigeonCodec() {} @Override protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { switch (type) { - case (byte) 129: - { - Object value = readValue(buffer); - return value == null ? null : SourceCamera.values()[((Long) value).intValue()]; - } - case (byte) 130: - { - Object value = readValue(buffer); - return value == null ? null : SourceType.values()[((Long) value).intValue()]; - } - case (byte) 131: - { - Object value = readValue(buffer); - return value == null ? null : CacheRetrievalType.values()[((Long) value).intValue()]; - } + case (byte) 129: { + Object value = readValue(buffer); + return value == null ? null : SourceCamera.values()[((Long) value).intValue()]; + } + case (byte) 130: { + Object value = readValue(buffer); + return value == null ? null : SourceType.values()[((Long) value).intValue()]; + } + case (byte) 131: { + Object value = readValue(buffer); + return value == null ? null : CacheRetrievalType.values()[((Long) value).intValue()]; + } case (byte) 132: return GeneralOptions.fromList((ArrayList) readValue(buffer)); case (byte) 133: @@ -862,6 +830,7 @@ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { } } + /** Asynchronous error handling return type for non-nullable API method returns. */ public interface Result { /** Success case callback method for handling returns. */ @@ -889,49 +858,30 @@ public interface VoidResult { /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface ImagePickerApi { /** Selects images and returns their paths. */ - void pickImages( - @NonNull SourceSpecification source, - @NonNull ImageSelectionOptions options, - @NonNull GeneralOptions generalOptions, - @NonNull Result> result); + void pickImages(@NonNull SourceSpecification source, @NonNull ImageSelectionOptions options, @NonNull GeneralOptions generalOptions, @NonNull Result> result); /** Selects video and returns their paths. */ - void pickVideos( - @NonNull SourceSpecification source, - @NonNull VideoSelectionOptions options, - @NonNull GeneralOptions generalOptions, - @NonNull Result> result); + void pickVideos(@NonNull SourceSpecification source, @NonNull VideoSelectionOptions options, @NonNull GeneralOptions generalOptions, @NonNull Result> result); /** Selects images and videos and returns their paths. */ - void pickMedia( - @NonNull MediaSelectionOptions mediaSelectionOptions, - @NonNull GeneralOptions generalOptions, - @NonNull Result> result); + void pickMedia(@NonNull MediaSelectionOptions mediaSelectionOptions, @NonNull GeneralOptions generalOptions, @NonNull Result> result); /** Returns results from a previous app session, if any. */ - @Nullable + @Nullable CacheRetrievalResult retrieveLostResults(); /** The codec used by ImagePickerApi. */ static @NonNull MessageCodec getCodec() { return PigeonCodec.INSTANCE; } - /** Sets up an instance of `ImagePickerApi` to handle messages through the `binaryMessenger`. */ + /**Sets up an instance of `ImagePickerApi` to handle messages through the `binaryMessenger`. */ static void setUp(@NonNull BinaryMessenger binaryMessenger, @Nullable ImagePickerApi api) { setUp(binaryMessenger, "", api); } - - static void setUp( - @NonNull BinaryMessenger binaryMessenger, - @NonNull String messageChannelSuffix, - @Nullable ImagePickerApi api) { + static void setUp(@NonNull BinaryMessenger binaryMessenger, @NonNull String messageChannelSuffix, @Nullable ImagePickerApi api) { messageChannelSuffix = messageChannelSuffix.isEmpty() ? "" : "." + messageChannelSuffix; { BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages" - + messageChannelSuffix, - getCodec(), - taskQueue); + binaryMessenger, "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages" + messageChannelSuffix, getCodec(), taskQueue); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -963,11 +913,7 @@ public void error(Throwable error) { BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos" - + messageChannelSuffix, - getCodec(), - taskQueue); + binaryMessenger, "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos" + messageChannelSuffix, getCodec(), taskQueue); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -998,17 +944,13 @@ public void error(Throwable error) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia" - + messageChannelSuffix, - getCodec()); + binaryMessenger, "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia" + messageChannelSuffix, getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; - MediaSelectionOptions mediaSelectionOptionsArg = - (MediaSelectionOptions) args.get(0); + MediaSelectionOptions mediaSelectionOptionsArg = (MediaSelectionOptions) args.get(0); GeneralOptions generalOptionsArg = (GeneralOptions) args.get(1); Result> resultCallback = new Result>() { @@ -1033,11 +975,7 @@ public void error(Throwable error) { BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.image_picker_android.ImagePickerApi.retrieveLostResults" - + messageChannelSuffix, - getCodec(), - taskQueue); + binaryMessenger, "dev.flutter.pigeon.image_picker_android.ImagePickerApi.retrieveLostResults" + messageChannelSuffix, getCodec(), taskQueue); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -1045,7 +983,8 @@ public void error(Throwable error) { try { CacheRetrievalResult output = api.retrieveLostResults(); wrapped.add(0, output); - } catch (Throwable exception) { + } + catch (Throwable exception) { wrapped = wrapError(exception); } reply.reply(wrapped); diff --git a/packages/image_picker/image_picker_android/lib/src/messages.g.dart b/packages/image_picker/image_picker_android/lib/src/messages.g.dart index e221123fd800..e207878cdb91 100644 --- a/packages/image_picker/image_picker_android/lib/src/messages.g.dart +++ b/packages/image_picker/image_picker_android/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.4.1), do not edit directly. +// Autogenerated from Pigeon (v22.6.2), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers @@ -18,8 +18,7 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse( - {Object? result, PlatformException? error, bool empty = false}) { +List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; } @@ -247,6 +246,7 @@ class CacheRetrievalResult { } } + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -254,34 +254,34 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is SourceCamera) { + } else if (value is SourceCamera) { buffer.putUint8(129); writeValue(buffer, value.index); - } else if (value is SourceType) { + } else if (value is SourceType) { buffer.putUint8(130); writeValue(buffer, value.index); - } else if (value is CacheRetrievalType) { + } else if (value is CacheRetrievalType) { buffer.putUint8(131); writeValue(buffer, value.index); - } else if (value is GeneralOptions) { + } else if (value is GeneralOptions) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is ImageSelectionOptions) { + } else if (value is ImageSelectionOptions) { buffer.putUint8(133); writeValue(buffer, value.encode()); - } else if (value is MediaSelectionOptions) { + } else if (value is MediaSelectionOptions) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is VideoSelectionOptions) { + } else if (value is VideoSelectionOptions) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is SourceSpecification) { + } else if (value is SourceSpecification) { buffer.putUint8(136); writeValue(buffer, value.encode()); - } else if (value is CacheRetrievalError) { + } else if (value is CacheRetrievalError) { buffer.putUint8(137); writeValue(buffer, value.encode()); - } else if (value is CacheRetrievalResult) { + } else if (value is CacheRetrievalResult) { buffer.putUint8(138); writeValue(buffer, value.encode()); } else { @@ -292,28 +292,28 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: final int? value = readValue(buffer) as int?; return value == null ? null : SourceCamera.values[value]; - case 130: + case 130: final int? value = readValue(buffer) as int?; return value == null ? null : SourceType.values[value]; - case 131: + case 131: final int? value = readValue(buffer) as int?; return value == null ? null : CacheRetrievalType.values[value]; - case 132: + case 132: return GeneralOptions.decode(readValue(buffer)!); - case 133: + case 133: return ImageSelectionOptions.decode(readValue(buffer)!); - case 134: + case 134: return MediaSelectionOptions.decode(readValue(buffer)!); - case 135: + case 135: return VideoSelectionOptions.decode(readValue(buffer)!); - case 136: + case 136: return SourceSpecification.decode(readValue(buffer)!); - case 137: + case 137: return CacheRetrievalError.decode(readValue(buffer)!); - case 138: + case 138: return CacheRetrievalResult.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -325,11 +325,9 @@ class ImagePickerApi { /// Constructor for [ImagePickerApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - ImagePickerApi( - {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + ImagePickerApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -337,18 +335,15 @@ class ImagePickerApi { final String pigeonVar_messageChannelSuffix; /// Selects images and returns their paths. - Future> pickImages(SourceSpecification source, - ImageSelectionOptions options, GeneralOptions generalOptions) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + Future> pickImages(SourceSpecification source, ImageSelectionOptions options, GeneralOptions generalOptions) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([source, options, generalOptions]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([source, options, generalOptions]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -368,18 +363,15 @@ class ImagePickerApi { } /// Selects video and returns their paths. - Future> pickVideos(SourceSpecification source, - VideoSelectionOptions options, GeneralOptions generalOptions) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + Future> pickVideos(SourceSpecification source, VideoSelectionOptions options, GeneralOptions generalOptions) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([source, options, generalOptions]) as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([source, options, generalOptions]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -399,19 +391,15 @@ class ImagePickerApi { } /// Selects images and videos and returns their paths. - Future> pickMedia(MediaSelectionOptions mediaSelectionOptions, - GeneralOptions generalOptions) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + Future> pickMedia(MediaSelectionOptions mediaSelectionOptions, GeneralOptions generalOptions) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([mediaSelectionOptions, generalOptions]) - as List?; + final List? pigeonVar_replyList = + await pigeonVar_channel.send([mediaSelectionOptions, generalOptions]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -432,10 +420,8 @@ class ImagePickerApi { /// Returns results from a previous app session, if any. Future retrieveLostResults() async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.retrieveLostResults$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final String pigeonVar_channelName = 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.retrieveLostResults$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, diff --git a/packages/image_picker/image_picker_android/test/test_api.g.dart b/packages/image_picker/image_picker_android/test/test_api.g.dart index 4343c4958436..695335c4d497 100644 --- a/packages/image_picker/image_picker_android/test/test_api.g.dart +++ b/packages/image_picker/image_picker_android/test/test_api.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.4.1), do not edit directly. +// Autogenerated from Pigeon (v22.6.2), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import, no_leading_underscores_for_local_identifiers // ignore_for_file: avoid_relative_lib_imports @@ -13,6 +13,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:image_picker_android/src/messages.g.dart'; + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -20,34 +21,34 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is SourceCamera) { + } else if (value is SourceCamera) { buffer.putUint8(129); writeValue(buffer, value.index); - } else if (value is SourceType) { + } else if (value is SourceType) { buffer.putUint8(130); writeValue(buffer, value.index); - } else if (value is CacheRetrievalType) { + } else if (value is CacheRetrievalType) { buffer.putUint8(131); writeValue(buffer, value.index); - } else if (value is GeneralOptions) { + } else if (value is GeneralOptions) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is ImageSelectionOptions) { + } else if (value is ImageSelectionOptions) { buffer.putUint8(133); writeValue(buffer, value.encode()); - } else if (value is MediaSelectionOptions) { + } else if (value is MediaSelectionOptions) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is VideoSelectionOptions) { + } else if (value is VideoSelectionOptions) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is SourceSpecification) { + } else if (value is SourceSpecification) { buffer.putUint8(136); writeValue(buffer, value.encode()); - } else if (value is CacheRetrievalError) { + } else if (value is CacheRetrievalError) { buffer.putUint8(137); writeValue(buffer, value.encode()); - } else if (value is CacheRetrievalResult) { + } else if (value is CacheRetrievalResult) { buffer.putUint8(138); writeValue(buffer, value.encode()); } else { @@ -58,28 +59,28 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: final int? value = readValue(buffer) as int?; return value == null ? null : SourceCamera.values[value]; - case 130: + case 130: final int? value = readValue(buffer) as int?; return value == null ? null : SourceType.values[value]; - case 131: + case 131: final int? value = readValue(buffer) as int?; return value == null ? null : CacheRetrievalType.values[value]; - case 132: + case 132: return GeneralOptions.decode(readValue(buffer)!); - case 133: + case 133: return ImageSelectionOptions.decode(readValue(buffer)!); - case 134: + case 134: return MediaSelectionOptions.decode(readValue(buffer)!); - case 135: + case 135: return VideoSelectionOptions.decode(readValue(buffer)!); - case 136: + case 136: return SourceSpecification.decode(readValue(buffer)!); - case 137: + case 137: return CacheRetrievalError.decode(readValue(buffer)!); - case 138: + case 138: return CacheRetrievalResult.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -88,176 +89,128 @@ class _PigeonCodec extends StandardMessageCodec { } abstract class TestHostImagePickerApi { - static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => - TestDefaultBinaryMessengerBinding.instance; + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); /// Selects images and returns their paths. - Future> pickImages(SourceSpecification source, - ImageSelectionOptions options, GeneralOptions generalOptions); + Future> pickImages(SourceSpecification source, ImageSelectionOptions options, GeneralOptions generalOptions); /// Selects video and returns their paths. - Future> pickVideos(SourceSpecification source, - VideoSelectionOptions options, GeneralOptions generalOptions); + Future> pickVideos(SourceSpecification source, VideoSelectionOptions options, GeneralOptions generalOptions); /// Selects images and videos and returns their paths. - Future> pickMedia(MediaSelectionOptions mediaSelectionOptions, - GeneralOptions generalOptions); + Future> pickMedia(MediaSelectionOptions mediaSelectionOptions, GeneralOptions generalOptions); /// Returns results from a previous app session, if any. CacheRetrievalResult? retrieveLostResults(); - static void setUp( - TestHostImagePickerApi? api, { - BinaryMessenger? binaryMessenger, - String messageChannelSuffix = '', - }) { - messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + static void setUp(TestHostImagePickerApi? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages was null.'); + 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages was null.'); final List args = (message as List?)!; - final SourceSpecification? arg_source = - (args[0] as SourceSpecification?); + final SourceSpecification? arg_source = (args[0] as SourceSpecification?); assert(arg_source != null, 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages was null, expected non-null SourceSpecification.'); - final ImageSelectionOptions? arg_options = - (args[1] as ImageSelectionOptions?); + final ImageSelectionOptions? arg_options = (args[1] as ImageSelectionOptions?); assert(arg_options != null, 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages was null, expected non-null ImageSelectionOptions.'); - final GeneralOptions? arg_generalOptions = - (args[2] as GeneralOptions?); + final GeneralOptions? arg_generalOptions = (args[2] as GeneralOptions?); assert(arg_generalOptions != null, 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages was null, expected non-null GeneralOptions.'); try { - final List output = await api.pickImages( - arg_source!, arg_options!, arg_generalOptions!); + final List output = await api.pickImages(arg_source!, arg_options!, arg_generalOptions!); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos was null.'); + 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos was null.'); final List args = (message as List?)!; - final SourceSpecification? arg_source = - (args[0] as SourceSpecification?); + final SourceSpecification? arg_source = (args[0] as SourceSpecification?); assert(arg_source != null, 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos was null, expected non-null SourceSpecification.'); - final VideoSelectionOptions? arg_options = - (args[1] as VideoSelectionOptions?); + final VideoSelectionOptions? arg_options = (args[1] as VideoSelectionOptions?); assert(arg_options != null, 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos was null, expected non-null VideoSelectionOptions.'); - final GeneralOptions? arg_generalOptions = - (args[2] as GeneralOptions?); + final GeneralOptions? arg_generalOptions = (args[2] as GeneralOptions?); assert(arg_generalOptions != null, 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos was null, expected non-null GeneralOptions.'); try { - final List output = await api.pickVideos( - arg_source!, arg_options!, arg_generalOptions!); + final List output = await api.pickVideos(arg_source!, arg_options!, arg_generalOptions!); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia was null.'); + 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia was null.'); final List args = (message as List?)!; - final MediaSelectionOptions? arg_mediaSelectionOptions = - (args[0] as MediaSelectionOptions?); + final MediaSelectionOptions? arg_mediaSelectionOptions = (args[0] as MediaSelectionOptions?); assert(arg_mediaSelectionOptions != null, 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia was null, expected non-null MediaSelectionOptions.'); - final GeneralOptions? arg_generalOptions = - (args[1] as GeneralOptions?); + final GeneralOptions? arg_generalOptions = (args[1] as GeneralOptions?); assert(arg_generalOptions != null, 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia was null, expected non-null GeneralOptions.'); try { - final List output = await api.pickMedia( - arg_mediaSelectionOptions!, arg_generalOptions!); + final List output = await api.pickMedia(arg_mediaSelectionOptions!, arg_generalOptions!); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel< - Object?> pigeonVar_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.retrieveLostResults$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.retrieveLostResults$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(pigeonVar_channel, - (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { try { final CacheRetrievalResult? output = api.retrieveLostResults(); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } From 8ced272fd0afee57129f43c457f801f7851b859d Mon Sep 17 00:00:00 2001 From: Gray Mackall Date: Wed, 27 Nov 2024 13:33:44 -0800 Subject: [PATCH 4/6] mirror Reid's change --- .../main/java/io/flutter/plugins/imagepicker/FileUtils.java | 5 ++++- .../java/io/flutter/plugins/imagepicker/FileUtilTest.java | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java index 4dd196a33294..3f573e044236 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java @@ -138,7 +138,10 @@ private static String getImageExtension(Context context, Uri uriImage) { return fileName; } - // From https://developer.android.com/privacy-and-security/risks/path-traversal#path-traversal-mitigations. + /** + * Use with file name sanitization and an non-guessable directory. + * From .... + */ protected static @NonNull File saferOpenFile(@NonNull String path, @NonNull String expectedDir) throws IllegalArgumentException, IOException { File f = new File(path); String canonicalPath = f.getCanonicalPath(); diff --git a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java index e60e6406d44f..378735ff8515 100644 --- a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java +++ b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java @@ -6,6 +6,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -139,6 +140,7 @@ public void getPathFromUri_sanitizesPathIndirection() { String path = fileUtils.getPathFromUri(context, uri); assertNotNull(path); assertTrue(path.endsWith("_bar.png")); + assertFalse(path.contains("..")); } @Test From 79d63132158ec301b365595220a67622f631a6f5 Mon Sep 17 00:00:00 2001 From: Gray Mackall Date: Wed, 27 Nov 2024 13:49:17 -0800 Subject: [PATCH 5/6] do the null thing for now, fix after thanksgiving :) --- .../main/java/io/flutter/plugins/imagepicker/FileUtils.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java index 3f573e044236..1c8c15d802c3 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java @@ -96,6 +96,10 @@ String getPathFromUri(final Context context, final Uri uri) { // // See https://github.com/flutter/flutter/issues/100025 for more details. return null; + } catch (IllegalArgumentException e) { + // This is likely a result of an IllegalArgumentException that we have thrown in + // saferOpenFile(). TODO(gmackall): surface this error in dart. + return null; } } From c57bc8360805790f47ef7eb04c3d77b5c23dc667 Mon Sep 17 00:00:00 2001 From: Gray Mackall Date: Wed, 27 Nov 2024 13:57:20 -0800 Subject: [PATCH 6/6] format --- .../plugins/imagepicker/FileUtils.java | 25 ++- .../flutter/plugins/imagepicker/Messages.java | 175 +++++++++++------ .../plugins/imagepicker/FileUtilTest.java | 21 ++- .../lib/src/messages.g.dart | 96 ++++++---- .../image_picker_android/test/test_api.g.dart | 177 +++++++++++------- 5 files changed, 313 insertions(+), 181 deletions(-) diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java index 1c8c15d802c3..b24b6e5bd9ef 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java @@ -29,10 +29,8 @@ import android.net.Uri; import android.provider.MediaStore; import android.webkit.MimeTypeMap; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import io.flutter.Log; import java.io.File; import java.io.FileOutputStream; @@ -47,9 +45,10 @@ class FileUtils { * file name if possible. * *

If the filename contains path indirection or separators (.. or /), the end file name will be - * the segment after the final separator, with indirection replaced by underscores. - * E.g. "example/../..file.png" -> "_file.png". - * See: Improperly trusting ContentProvider-provided filename. + * the segment after the final separator, with indirection replaced by underscores. E.g. + * "example/../..file.png" -> "_file.png". See: Improperly + * trusting ContentProvider-provided filename. * *

Each file is placed in its own directory to avoid conflicts according to the following * scheme: {cacheDir}/{randomUuid}/{fileName} @@ -133,7 +132,7 @@ private static String getImageExtension(Context context, Uri uriImage) { return null; } - String[] badCharacters = new String[] { "..", "/" }; + String[] badCharacters = new String[] {"..", "/"}; String[] segments = displayName.split("/"); String fileName = segments[segments.length - 1]; for (String suspString : badCharacters) { @@ -143,14 +142,20 @@ private static String getImageExtension(Context context, Uri uriImage) { } /** - * Use with file name sanitization and an non-guessable directory. - * From .... + * Use with file name sanitization and an non-guessable directory. From .... */ - protected static @NonNull File saferOpenFile(@NonNull String path, @NonNull String expectedDir) throws IllegalArgumentException, IOException { + protected static @NonNull File saferOpenFile(@NonNull String path, @NonNull String expectedDir) + throws IllegalArgumentException, IOException { File f = new File(path); String canonicalPath = f.getCanonicalPath(); if (!canonicalPath.startsWith(expectedDir)) { - throw new IllegalArgumentException("Trying to open path outside of the expected directory. File: " + f.getCanonicalPath() + " was expected to be within directory: " + expectedDir + "."); + throw new IllegalArgumentException( + "Trying to open path outside of the expected directory. File: " + + f.getCanonicalPath() + + " was expected to be within directory: " + + expectedDir + + "."); } return f; } diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java index e2f4a541b13e..5c72a30b5915 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/Messages.java @@ -21,11 +21,7 @@ import java.lang.annotation.Target; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; /** Generated class from Pigeon. */ @@ -41,8 +37,7 @@ public static class FlutterError extends RuntimeException { /** The error details. Must be a datatype supported by the api codec. */ public final Object details; - public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) - { + public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) { super(message); this.code = code; this.details = details; @@ -61,7 +56,7 @@ protected static ArrayList wrapError(@NonNull Throwable exception) { errorList.add(exception.toString()); errorList.add(exception.getClass().getSimpleName()); errorList.add( - "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); } return errorList; } @@ -146,10 +141,16 @@ public void setLimit(@Nullable Long setterArg) { @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } GeneralOptions that = (GeneralOptions) o; - return allowMultiple.equals(that.allowMultiple) && usePhotoPicker.equals(that.usePhotoPicker) && Objects.equals(limit, that.limit); + return allowMultiple.equals(that.allowMultiple) + && usePhotoPicker.equals(that.usePhotoPicker) + && Objects.equals(limit, that.limit); } @Override @@ -216,7 +217,7 @@ ArrayList toList() { /** * Options for image selection and output. * - * Generated class from Pigeon that represents data sent in messages. + *

Generated class from Pigeon that represents data sent in messages. */ public static final class ImageSelectionOptions { /** If set, the max width that the image should be resized to fit in. */ @@ -244,7 +245,7 @@ public void setMaxHeight(@Nullable Double setterArg) { /** * The quality of the output image, from 0-100. * - * 100 indicates original quality. + *

100 indicates original quality. */ private @NonNull Long quality; @@ -264,10 +265,16 @@ public void setQuality(@NonNull Long setterArg) { @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } ImageSelectionOptions that = (ImageSelectionOptions) o; - return Objects.equals(maxWidth, that.maxWidth) && Objects.equals(maxHeight, that.maxHeight) && quality.equals(that.quality); + return Objects.equals(maxWidth, that.maxWidth) + && Objects.equals(maxHeight, that.maxHeight) + && quality.equals(that.quality); } @Override @@ -351,8 +358,12 @@ public void setImageSelectionOptions(@NonNull ImageSelectionOptions setterArg) { @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } MediaSelectionOptions that = (MediaSelectionOptions) o; return imageSelectionOptions.equals(that.imageSelectionOptions); } @@ -397,7 +408,7 @@ ArrayList toList() { /** * Options for image selection and output. * - * Generated class from Pigeon that represents data sent in messages. + *

Generated class from Pigeon that represents data sent in messages. */ public static final class VideoSelectionOptions { /** The maximum desired length for the video, in seconds. */ @@ -413,8 +424,12 @@ public void setMaxDurationSeconds(@Nullable Long setterArg) { @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } VideoSelectionOptions that = (VideoSelectionOptions) o; return Objects.equals(maxDurationSeconds, that.maxDurationSeconds); } @@ -459,7 +474,7 @@ ArrayList toList() { /** * Specification for the source of an image or video selection. * - * Generated class from Pigeon that represents data sent in messages. + *

Generated class from Pigeon that represents data sent in messages. */ public static final class SourceSpecification { private @NonNull SourceType type; @@ -490,8 +505,12 @@ public void setCamera(@Nullable SourceCamera setterArg) { @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } SourceSpecification that = (SourceSpecification) o; return type.equals(that.type) && Objects.equals(camera, that.camera); } @@ -548,9 +567,9 @@ ArrayList toList() { /** * An error that occurred during lost result retrieval. * - * The data here maps to the `PlatformException` that will be created from it. + *

The data here maps to the `PlatformException` that will be created from it. * - * Generated class from Pigeon that represents data sent in messages. + *

Generated class from Pigeon that represents data sent in messages. */ public static final class CacheRetrievalError { private @NonNull String code; @@ -581,8 +600,12 @@ public void setMessage(@Nullable String setterArg) { @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } CacheRetrievalError that = (CacheRetrievalError) o; return code.equals(that.code) && Objects.equals(message, that.message); } @@ -639,7 +662,7 @@ ArrayList toList() { /** * The result of retrieving cached results from a previous run. * - * Generated class from Pigeon that represents data sent in messages. + *

Generated class from Pigeon that represents data sent in messages. */ public static final class CacheRetrievalResult { /** The type of the retrieved data. */ @@ -686,10 +709,16 @@ public void setPaths(@NonNull List setterArg) { @Override public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } CacheRetrievalResult that = (CacheRetrievalResult) o; - return type.equals(that.type) && Objects.equals(error, that.error) && paths.equals(that.paths); + return type.equals(that.type) + && Objects.equals(error, that.error) + && paths.equals(that.paths); } @Override @@ -761,18 +790,21 @@ private PigeonCodec() {} @Override protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { switch (type) { - case (byte) 129: { - Object value = readValue(buffer); - return value == null ? null : SourceCamera.values()[((Long) value).intValue()]; - } - case (byte) 130: { - Object value = readValue(buffer); - return value == null ? null : SourceType.values()[((Long) value).intValue()]; - } - case (byte) 131: { - Object value = readValue(buffer); - return value == null ? null : CacheRetrievalType.values()[((Long) value).intValue()]; - } + case (byte) 129: + { + Object value = readValue(buffer); + return value == null ? null : SourceCamera.values()[((Long) value).intValue()]; + } + case (byte) 130: + { + Object value = readValue(buffer); + return value == null ? null : SourceType.values()[((Long) value).intValue()]; + } + case (byte) 131: + { + Object value = readValue(buffer); + return value == null ? null : CacheRetrievalType.values()[((Long) value).intValue()]; + } case (byte) 132: return GeneralOptions.fromList((ArrayList) readValue(buffer)); case (byte) 133: @@ -830,7 +862,6 @@ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { } } - /** Asynchronous error handling return type for non-nullable API method returns. */ public interface Result { /** Success case callback method for handling returns. */ @@ -858,30 +889,49 @@ public interface VoidResult { /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface ImagePickerApi { /** Selects images and returns their paths. */ - void pickImages(@NonNull SourceSpecification source, @NonNull ImageSelectionOptions options, @NonNull GeneralOptions generalOptions, @NonNull Result> result); + void pickImages( + @NonNull SourceSpecification source, + @NonNull ImageSelectionOptions options, + @NonNull GeneralOptions generalOptions, + @NonNull Result> result); /** Selects video and returns their paths. */ - void pickVideos(@NonNull SourceSpecification source, @NonNull VideoSelectionOptions options, @NonNull GeneralOptions generalOptions, @NonNull Result> result); + void pickVideos( + @NonNull SourceSpecification source, + @NonNull VideoSelectionOptions options, + @NonNull GeneralOptions generalOptions, + @NonNull Result> result); /** Selects images and videos and returns their paths. */ - void pickMedia(@NonNull MediaSelectionOptions mediaSelectionOptions, @NonNull GeneralOptions generalOptions, @NonNull Result> result); + void pickMedia( + @NonNull MediaSelectionOptions mediaSelectionOptions, + @NonNull GeneralOptions generalOptions, + @NonNull Result> result); /** Returns results from a previous app session, if any. */ - @Nullable + @Nullable CacheRetrievalResult retrieveLostResults(); /** The codec used by ImagePickerApi. */ static @NonNull MessageCodec getCodec() { return PigeonCodec.INSTANCE; } - /**Sets up an instance of `ImagePickerApi` to handle messages through the `binaryMessenger`. */ + /** Sets up an instance of `ImagePickerApi` to handle messages through the `binaryMessenger`. */ static void setUp(@NonNull BinaryMessenger binaryMessenger, @Nullable ImagePickerApi api) { setUp(binaryMessenger, "", api); } - static void setUp(@NonNull BinaryMessenger binaryMessenger, @NonNull String messageChannelSuffix, @Nullable ImagePickerApi api) { + + static void setUp( + @NonNull BinaryMessenger binaryMessenger, + @NonNull String messageChannelSuffix, + @Nullable ImagePickerApi api) { messageChannelSuffix = messageChannelSuffix.isEmpty() ? "" : "." + messageChannelSuffix; { BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages" + messageChannelSuffix, getCodec(), taskQueue); + binaryMessenger, + "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages" + + messageChannelSuffix, + getCodec(), + taskQueue); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -913,7 +963,11 @@ public void error(Throwable error) { BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos" + messageChannelSuffix, getCodec(), taskQueue); + binaryMessenger, + "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos" + + messageChannelSuffix, + getCodec(), + taskQueue); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -944,13 +998,17 @@ public void error(Throwable error) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia" + messageChannelSuffix, getCodec()); + binaryMessenger, + "dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia" + + messageChannelSuffix, + getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; - MediaSelectionOptions mediaSelectionOptionsArg = (MediaSelectionOptions) args.get(0); + MediaSelectionOptions mediaSelectionOptionsArg = + (MediaSelectionOptions) args.get(0); GeneralOptions generalOptionsArg = (GeneralOptions) args.get(1); Result> resultCallback = new Result>() { @@ -975,7 +1033,11 @@ public void error(Throwable error) { BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.image_picker_android.ImagePickerApi.retrieveLostResults" + messageChannelSuffix, getCodec(), taskQueue); + binaryMessenger, + "dev.flutter.pigeon.image_picker_android.ImagePickerApi.retrieveLostResults" + + messageChannelSuffix, + getCodec(), + taskQueue); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -983,8 +1045,7 @@ public void error(Throwable error) { try { CacheRetrievalResult output = api.retrieveLostResults(); wrapped.add(0, output); - } - catch (Throwable exception) { + } catch (Throwable exception) { wrapped = wrapError(exception); } reply.reply(wrapped); diff --git a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java index 378735ff8515..1a33302e1fdc 100644 --- a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java +++ b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java @@ -136,7 +136,7 @@ public void getPathFromUri_sanitizesPathIndirection() { Uri uri = Uri.parse(MockMaliciousContentProvider.PNG_URI); Robolectric.buildContentProvider(MockMaliciousContentProvider.class).create("dummy"); shadowContentResolver.registerInputStream( - uri, new ByteArrayInputStream("fileStream".getBytes(UTF_8))); + uri, new ByteArrayInputStream("fileStream".getBytes(UTF_8))); String path = fileUtils.getPathFromUri(context, uri); assertNotNull(path); assertTrue(path.endsWith("_bar.png")); @@ -222,11 +222,11 @@ public boolean onCreate() { @Nullable @Override public Cursor query( - @NonNull Uri uri, - @Nullable String[] projection, - @Nullable String selection, - @Nullable String[] selectionArgs, - @Nullable String sortOrder) { + @NonNull Uri uri, + @Nullable String[] projection, + @Nullable String selection, + @Nullable String[] selectionArgs, + @Nullable String sortOrder) { MatrixCursor cursor = new MatrixCursor(new String[] {MediaStore.MediaColumns.DISPLAY_NAME}); cursor.addRow(new Object[] {"foo/../..bar.png"}); return cursor; @@ -245,12 +245,17 @@ public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { } @Override - public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { + public int delete( + @NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { return 0; } @Override - public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { + public int update( + @NonNull Uri uri, + @Nullable ContentValues values, + @Nullable String selection, + @Nullable String[] selectionArgs) { return 0; } } diff --git a/packages/image_picker/image_picker_android/lib/src/messages.g.dart b/packages/image_picker/image_picker_android/lib/src/messages.g.dart index e207878cdb91..cbeb6a2e61a1 100644 --- a/packages/image_picker/image_picker_android/lib/src/messages.g.dart +++ b/packages/image_picker/image_picker_android/lib/src/messages.g.dart @@ -18,7 +18,8 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { +List wrapResponse( + {Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; } @@ -246,7 +247,6 @@ class CacheRetrievalResult { } } - class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -254,34 +254,34 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is SourceCamera) { + } else if (value is SourceCamera) { buffer.putUint8(129); writeValue(buffer, value.index); - } else if (value is SourceType) { + } else if (value is SourceType) { buffer.putUint8(130); writeValue(buffer, value.index); - } else if (value is CacheRetrievalType) { + } else if (value is CacheRetrievalType) { buffer.putUint8(131); writeValue(buffer, value.index); - } else if (value is GeneralOptions) { + } else if (value is GeneralOptions) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is ImageSelectionOptions) { + } else if (value is ImageSelectionOptions) { buffer.putUint8(133); writeValue(buffer, value.encode()); - } else if (value is MediaSelectionOptions) { + } else if (value is MediaSelectionOptions) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is VideoSelectionOptions) { + } else if (value is VideoSelectionOptions) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is SourceSpecification) { + } else if (value is SourceSpecification) { buffer.putUint8(136); writeValue(buffer, value.encode()); - } else if (value is CacheRetrievalError) { + } else if (value is CacheRetrievalError) { buffer.putUint8(137); writeValue(buffer, value.encode()); - } else if (value is CacheRetrievalResult) { + } else if (value is CacheRetrievalResult) { buffer.putUint8(138); writeValue(buffer, value.encode()); } else { @@ -292,28 +292,28 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: final int? value = readValue(buffer) as int?; return value == null ? null : SourceCamera.values[value]; - case 130: + case 130: final int? value = readValue(buffer) as int?; return value == null ? null : SourceType.values[value]; - case 131: + case 131: final int? value = readValue(buffer) as int?; return value == null ? null : CacheRetrievalType.values[value]; - case 132: + case 132: return GeneralOptions.decode(readValue(buffer)!); - case 133: + case 133: return ImageSelectionOptions.decode(readValue(buffer)!); - case 134: + case 134: return MediaSelectionOptions.decode(readValue(buffer)!); - case 135: + case 135: return VideoSelectionOptions.decode(readValue(buffer)!); - case 136: + case 136: return SourceSpecification.decode(readValue(buffer)!); - case 137: + case 137: return CacheRetrievalError.decode(readValue(buffer)!); - case 138: + case 138: return CacheRetrievalResult.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -325,9 +325,11 @@ class ImagePickerApi { /// Constructor for [ImagePickerApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - ImagePickerApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + ImagePickerApi( + {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -335,15 +337,18 @@ class ImagePickerApi { final String pigeonVar_messageChannelSuffix; /// Selects images and returns their paths. - Future> pickImages(SourceSpecification source, ImageSelectionOptions options, GeneralOptions generalOptions) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + Future> pickImages(SourceSpecification source, + ImageSelectionOptions options, GeneralOptions generalOptions) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([source, options, generalOptions]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([source, options, generalOptions]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -363,15 +368,18 @@ class ImagePickerApi { } /// Selects video and returns their paths. - Future> pickVideos(SourceSpecification source, VideoSelectionOptions options, GeneralOptions generalOptions) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + Future> pickVideos(SourceSpecification source, + VideoSelectionOptions options, GeneralOptions generalOptions) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([source, options, generalOptions]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([source, options, generalOptions]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -391,15 +399,19 @@ class ImagePickerApi { } /// Selects images and videos and returns their paths. - Future> pickMedia(MediaSelectionOptions mediaSelectionOptions, GeneralOptions generalOptions) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + Future> pickMedia(MediaSelectionOptions mediaSelectionOptions, + GeneralOptions generalOptions) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([mediaSelectionOptions, generalOptions]) as List?; + final List? pigeonVar_replyList = await pigeonVar_channel + .send([mediaSelectionOptions, generalOptions]) + as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -420,8 +432,10 @@ class ImagePickerApi { /// Returns results from a previous app session, if any. Future retrieveLostResults() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.retrieveLostResults$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.retrieveLostResults$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, diff --git a/packages/image_picker/image_picker_android/test/test_api.g.dart b/packages/image_picker/image_picker_android/test/test_api.g.dart index 695335c4d497..f28bafe9ea7f 100644 --- a/packages/image_picker/image_picker_android/test/test_api.g.dart +++ b/packages/image_picker/image_picker_android/test/test_api.g.dart @@ -13,7 +13,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:image_picker_android/src/messages.g.dart'; - class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -21,34 +20,34 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is SourceCamera) { + } else if (value is SourceCamera) { buffer.putUint8(129); writeValue(buffer, value.index); - } else if (value is SourceType) { + } else if (value is SourceType) { buffer.putUint8(130); writeValue(buffer, value.index); - } else if (value is CacheRetrievalType) { + } else if (value is CacheRetrievalType) { buffer.putUint8(131); writeValue(buffer, value.index); - } else if (value is GeneralOptions) { + } else if (value is GeneralOptions) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is ImageSelectionOptions) { + } else if (value is ImageSelectionOptions) { buffer.putUint8(133); writeValue(buffer, value.encode()); - } else if (value is MediaSelectionOptions) { + } else if (value is MediaSelectionOptions) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is VideoSelectionOptions) { + } else if (value is VideoSelectionOptions) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is SourceSpecification) { + } else if (value is SourceSpecification) { buffer.putUint8(136); writeValue(buffer, value.encode()); - } else if (value is CacheRetrievalError) { + } else if (value is CacheRetrievalError) { buffer.putUint8(137); writeValue(buffer, value.encode()); - } else if (value is CacheRetrievalResult) { + } else if (value is CacheRetrievalResult) { buffer.putUint8(138); writeValue(buffer, value.encode()); } else { @@ -59,28 +58,28 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: final int? value = readValue(buffer) as int?; return value == null ? null : SourceCamera.values[value]; - case 130: + case 130: final int? value = readValue(buffer) as int?; return value == null ? null : SourceType.values[value]; - case 131: + case 131: final int? value = readValue(buffer) as int?; return value == null ? null : CacheRetrievalType.values[value]; - case 132: + case 132: return GeneralOptions.decode(readValue(buffer)!); - case 133: + case 133: return ImageSelectionOptions.decode(readValue(buffer)!); - case 134: + case 134: return MediaSelectionOptions.decode(readValue(buffer)!); - case 135: + case 135: return VideoSelectionOptions.decode(readValue(buffer)!); - case 136: + case 136: return SourceSpecification.decode(readValue(buffer)!); - case 137: + case 137: return CacheRetrievalError.decode(readValue(buffer)!); - case 138: + case 138: return CacheRetrievalResult.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -89,128 +88,176 @@ class _PigeonCodec extends StandardMessageCodec { } abstract class TestHostImagePickerApi { - static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); /// Selects images and returns their paths. - Future> pickImages(SourceSpecification source, ImageSelectionOptions options, GeneralOptions generalOptions); + Future> pickImages(SourceSpecification source, + ImageSelectionOptions options, GeneralOptions generalOptions); /// Selects video and returns their paths. - Future> pickVideos(SourceSpecification source, VideoSelectionOptions options, GeneralOptions generalOptions); + Future> pickVideos(SourceSpecification source, + VideoSelectionOptions options, GeneralOptions generalOptions); /// Selects images and videos and returns their paths. - Future> pickMedia(MediaSelectionOptions mediaSelectionOptions, GeneralOptions generalOptions); + Future> pickMedia(MediaSelectionOptions mediaSelectionOptions, + GeneralOptions generalOptions); /// Returns results from a previous app session, if any. CacheRetrievalResult? retrieveLostResults(); - static void setUp(TestHostImagePickerApi? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { - messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + static void setUp( + TestHostImagePickerApi? api, { + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) { + messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages was null.'); + 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages was null.'); final List args = (message as List?)!; - final SourceSpecification? arg_source = (args[0] as SourceSpecification?); + final SourceSpecification? arg_source = + (args[0] as SourceSpecification?); assert(arg_source != null, 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages was null, expected non-null SourceSpecification.'); - final ImageSelectionOptions? arg_options = (args[1] as ImageSelectionOptions?); + final ImageSelectionOptions? arg_options = + (args[1] as ImageSelectionOptions?); assert(arg_options != null, 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages was null, expected non-null ImageSelectionOptions.'); - final GeneralOptions? arg_generalOptions = (args[2] as GeneralOptions?); + final GeneralOptions? arg_generalOptions = + (args[2] as GeneralOptions?); assert(arg_generalOptions != null, 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickImages was null, expected non-null GeneralOptions.'); try { - final List output = await api.pickImages(arg_source!, arg_options!, arg_generalOptions!); + final List output = await api.pickImages( + arg_source!, arg_options!, arg_generalOptions!); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos was null.'); + 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos was null.'); final List args = (message as List?)!; - final SourceSpecification? arg_source = (args[0] as SourceSpecification?); + final SourceSpecification? arg_source = + (args[0] as SourceSpecification?); assert(arg_source != null, 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos was null, expected non-null SourceSpecification.'); - final VideoSelectionOptions? arg_options = (args[1] as VideoSelectionOptions?); + final VideoSelectionOptions? arg_options = + (args[1] as VideoSelectionOptions?); assert(arg_options != null, 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos was null, expected non-null VideoSelectionOptions.'); - final GeneralOptions? arg_generalOptions = (args[2] as GeneralOptions?); + final GeneralOptions? arg_generalOptions = + (args[2] as GeneralOptions?); assert(arg_generalOptions != null, 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickVideos was null, expected non-null GeneralOptions.'); try { - final List output = await api.pickVideos(arg_source!, arg_options!, arg_generalOptions!); + final List output = await api.pickVideos( + arg_source!, arg_options!, arg_generalOptions!); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia was null.'); + 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia was null.'); final List args = (message as List?)!; - final MediaSelectionOptions? arg_mediaSelectionOptions = (args[0] as MediaSelectionOptions?); + final MediaSelectionOptions? arg_mediaSelectionOptions = + (args[0] as MediaSelectionOptions?); assert(arg_mediaSelectionOptions != null, 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia was null, expected non-null MediaSelectionOptions.'); - final GeneralOptions? arg_generalOptions = (args[1] as GeneralOptions?); + final GeneralOptions? arg_generalOptions = + (args[1] as GeneralOptions?); assert(arg_generalOptions != null, 'Argument for dev.flutter.pigeon.image_picker_android.ImagePickerApi.pickMedia was null, expected non-null GeneralOptions.'); try { - final List output = await api.pickMedia(arg_mediaSelectionOptions!, arg_generalOptions!); + final List output = await api.pickMedia( + arg_mediaSelectionOptions!, arg_generalOptions!); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.retrieveLostResults$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.image_picker_android.ImagePickerApi.retrieveLostResults$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, null); + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, null); } else { - _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(pigeonVar_channel, (Object? message) async { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(pigeonVar_channel, + (Object? message) async { try { final CacheRetrievalResult? output = api.retrieveLostResults(); return [output]; } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); }