From 0b0da58bff657f3208976566138e20a5126b9834 Mon Sep 17 00:00:00 2001 From: David Britch Date: Mon, 24 Mar 2025 14:03:38 +0000 Subject: [PATCH 1/4] Control Android FileProvider locations --- .../platform-integration/appmodel/launcher.md | 6 ++- .../communication/email.md | 6 ++- docs/platform-integration/data/share.md | 6 ++- .../includes/android-fileproviderpaths.md | 44 +++++++++++++++++++ 4 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 docs/platform-integration/includes/android-fileproviderpaths.md diff --git a/docs/platform-integration/appmodel/launcher.md b/docs/platform-integration/appmodel/launcher.md index 94e483947..3890b7d1e 100644 --- a/docs/platform-integration/appmodel/launcher.md +++ b/docs/platform-integration/appmodel/launcher.md @@ -1,7 +1,7 @@ --- title: "Launcher" description: "Learn how to use the .NET MAUI ILauncher interface in the Microsoft.Maui.ApplicationModel namespace, which can open another application by URI." -ms.date: 02/02/2023 +ms.date: 03/24/2025 no-loc: ["Microsoft.Maui", "Microsoft.Maui.ApplicationModel"] --- @@ -84,6 +84,10 @@ The following code example writes text to a file, and opens the text file with t :::code language="csharp" source="../snippets/shared_1/AppModelPage.xaml.cs" id="launcher_open_file"::: +### Control file locations + +[!INCLUDE [android-fileproviderpaths](../includes/android-fileproviderpaths.md)] + ## Set the launcher location [!INCLUDE [ios-PresentationSourceBounds](../includes/ios-PresentationSourceBounds.md)] diff --git a/docs/platform-integration/communication/email.md b/docs/platform-integration/communication/email.md index 350be5de6..5adc14b8c 100644 --- a/docs/platform-integration/communication/email.md +++ b/docs/platform-integration/communication/email.md @@ -1,7 +1,7 @@ --- title: "Email" description: "Learn how to use the .NET MAUI IEmail interface in the Microsoft.Maui.ApplicationModel.Communication namespace to open the default email application. The subject, body, and recipients of an email can be set." -ms.date: 02/02/2023 +ms.date: 03/24/2025 no-loc: ["Microsoft.Maui", "Microsoft.Maui.ApplicationModel.Communication"] --- @@ -67,6 +67,10 @@ The following example demonstrates adding an image file to the email attachments :::code language="csharp" source="../snippets/shared_1/CommsPage.xaml.cs" id="email_picture" highlight="18"::: +### Control file locations + +[!INCLUDE [android-fileproviderpaths](../includes/android-fileproviderpaths.md)] + ## Platform Differences diff --git a/docs/platform-integration/data/share.md b/docs/platform-integration/data/share.md index ece59907c..d593100b5 100644 --- a/docs/platform-integration/data/share.md +++ b/docs/platform-integration/data/share.md @@ -1,7 +1,7 @@ --- title: "Share" description: "Learn how to use the .NET MAUI IShare interface, which can share data, such as web links, to other applications on the device." -ms.date: 02/02/2023 +ms.date: 03/24/2025 no-loc: ["Microsoft.Maui", "Microsoft.Maui.ApplicationModel.DataTransfer"] --- @@ -67,6 +67,10 @@ The following code example writes two text files to the device, and then request :::code language="csharp" source="../snippets/shared_1/DataPage.xaml.cs" id="share_file_multiple"::: +## Control file locations + +[!INCLUDE [android-fileproviderpaths](../includes/android-fileproviderpaths.md)] + ## Presentation location [!INCLUDE [ios-PresentationSourceBounds](../includes/ios-PresentationSourceBounds.md)] diff --git a/docs/platform-integration/includes/android-fileproviderpaths.md b/docs/platform-integration/includes/android-fileproviderpaths.md new file mode 100644 index 000000000..4fc15a0d4 --- /dev/null +++ b/docs/platform-integration/includes/android-fileproviderpaths.md @@ -0,0 +1,44 @@ +--- +ms.topic: include +ms.date: 03/24/2025 +--- + +> [!IMPORTANT] +> This section only applies to Android. + +In some scenarios on Android, such as when a file is in private storage, it can be copied into the app cache which is then shared via an Android `FileProvider`. However, this can [unintentionally expose the entire cache and app data](https://developer.android.com/privacy-and-security/risks/file-providers) to an attacker. This can be prevented by adding a file provider file paths override file to your app, and ensuring that files are copied to the location specified in this file prior to sharing. + +To add a file provider file paths override file to your app, add a file named *microsoft_maui_essentials_fileprovider_file_paths.xml* to the *Platforms\Android\Resources\xml* folder in your app. The full, relative file name to the project should be *Platforms\Android\Resources\xml\microsoft_maui_essentials_fileprovider_file_paths.xml*. Then, add XML to the file for your required paths: + +```xml + + + + + + +``` + +For more information about file provider paths, see [FileProvider](https://developer.android.com/reference/androidx/core/content/FileProvider) on developer.android.com. + +Prior to sharing a file, you should ensure it's first written to the *sharing-root* folder in one of the locations from the file provider file paths override file: + +```cs +// Write into the specific sub-directory +var dir = Path.Combine(FileSystem.CacheDirectory, "sharing-root"); +Directory.CreateDirectory(dir); +var file = Path.Combine(dir, "mydata.txt"); +await File.WriteAllTextAsync(file, $"My data: {count}"); + +// Share the file +await Launcher.OpenAsync(new OpenFileRequest +{ + Title = "My data", + File = new ReadOnlyFile(file), +}); +``` + +You can verify that the file is being shared correctly if the shared URI excludes the sharing root directory. For example, if you share the file */sharing-root/mydata.txt* and the shared URI is `content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/sharing-root/mydata.txt` then the file provider is not using the correct path. If the shared URI is `content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/mydata.txt` then the file provider is using the correct path. + +> [!WARNING] +> If when sharing a file you receive an `Java.Lang.IllegalArgumentException`, with a message similar to "Failed to find configured root that contains /data/data/com.companyname.overwritefileproviderpaths/cache/some-non-sharing-path/mydata.txt", you are most likely sharing a file that's outside of the sharing-root. From 5f4a89d346f754e019577cab21c36e3a3f7bf355 Mon Sep 17 00:00:00 2001 From: David Britch Date: Mon, 24 Mar 2025 14:06:14 +0000 Subject: [PATCH 2/4] Fix linting error. --- docs/platform-integration/includes/android-fileproviderpaths.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/platform-integration/includes/android-fileproviderpaths.md b/docs/platform-integration/includes/android-fileproviderpaths.md index 4fc15a0d4..1b2fc204e 100644 --- a/docs/platform-integration/includes/android-fileproviderpaths.md +++ b/docs/platform-integration/includes/android-fileproviderpaths.md @@ -38,7 +38,9 @@ await Launcher.OpenAsync(new OpenFileRequest }); ``` + You can verify that the file is being shared correctly if the shared URI excludes the sharing root directory. For example, if you share the file */sharing-root/mydata.txt* and the shared URI is `content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/sharing-root/mydata.txt` then the file provider is not using the correct path. If the shared URI is `content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/mydata.txt` then the file provider is using the correct path. + > [!WARNING] > If when sharing a file you receive an `Java.Lang.IllegalArgumentException`, with a message similar to "Failed to find configured root that contains /data/data/com.companyname.overwritefileproviderpaths/cache/some-non-sharing-path/mydata.txt", you are most likely sharing a file that's outside of the sharing-root. From 314a596e65f8614583ce3a83a248cb1ac8054e52 Mon Sep 17 00:00:00 2001 From: David Britch Date: Mon, 24 Mar 2025 14:17:48 +0000 Subject: [PATCH 3/4] Escape CacheDirectory. --- .../includes/android-fileproviderpaths.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/platform-integration/includes/android-fileproviderpaths.md b/docs/platform-integration/includes/android-fileproviderpaths.md index 1b2fc204e..324ea6dbc 100644 --- a/docs/platform-integration/includes/android-fileproviderpaths.md +++ b/docs/platform-integration/includes/android-fileproviderpaths.md @@ -38,9 +38,7 @@ await Launcher.OpenAsync(new OpenFileRequest }); ``` - -You can verify that the file is being shared correctly if the shared URI excludes the sharing root directory. For example, if you share the file */sharing-root/mydata.txt* and the shared URI is `content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/sharing-root/mydata.txt` then the file provider is not using the correct path. If the shared URI is `content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/mydata.txt` then the file provider is using the correct path. - +You can verify that the file is being shared correctly if the shared URI excludes the sharing root directory. For example, if you share the file *\/sharing-root/mydata.txt* and the shared URI is `content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/sharing-root/mydata.txt` then the file provider is not using the correct path. If the shared URI is `content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/mydata.txt` then the file provider is using the correct path. > [!WARNING] > If when sharing a file you receive an `Java.Lang.IllegalArgumentException`, with a message similar to "Failed to find configured root that contains /data/data/com.companyname.overwritefileproviderpaths/cache/some-non-sharing-path/mydata.txt", you are most likely sharing a file that's outside of the sharing-root. From e0f569b72bc27a509ddde055eb854bd1f047f5ea Mon Sep 17 00:00:00 2001 From: David Britch Date: Mon, 24 Mar 2025 14:22:53 +0000 Subject: [PATCH 4/4] Edits. --- .../includes/android-fileproviderpaths.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/platform-integration/includes/android-fileproviderpaths.md b/docs/platform-integration/includes/android-fileproviderpaths.md index 324ea6dbc..98b48ec46 100644 --- a/docs/platform-integration/includes/android-fileproviderpaths.md +++ b/docs/platform-integration/includes/android-fileproviderpaths.md @@ -8,7 +8,7 @@ ms.date: 03/24/2025 In some scenarios on Android, such as when a file is in private storage, it can be copied into the app cache which is then shared via an Android `FileProvider`. However, this can [unintentionally expose the entire cache and app data](https://developer.android.com/privacy-and-security/risks/file-providers) to an attacker. This can be prevented by adding a file provider file paths override file to your app, and ensuring that files are copied to the location specified in this file prior to sharing. -To add a file provider file paths override file to your app, add a file named *microsoft_maui_essentials_fileprovider_file_paths.xml* to the *Platforms\Android\Resources\xml* folder in your app. The full, relative file name to the project should be *Platforms\Android\Resources\xml\microsoft_maui_essentials_fileprovider_file_paths.xml*. Then, add XML to the file for your required paths: +To add a file provider file paths override file to your app, add a file named *microsoft_maui_essentials_fileprovider_file_paths.xml* to the *Platforms\Android\Resources\xml* folder in your app. Therefore, the full relative file name to the project should be *Platforms\Android\Resources\xml\microsoft_maui_essentials_fileprovider_file_paths.xml*. Then, add XML to the file for your required paths: ```xml @@ -21,7 +21,7 @@ To add a file provider file paths override file to your app, add a file named *m For more information about file provider paths, see [FileProvider](https://developer.android.com/reference/androidx/core/content/FileProvider) on developer.android.com. -Prior to sharing a file, you should ensure it's first written to the *sharing-root* folder in one of the locations from the file provider file paths override file: +Prior to sharing a file, you should ensure it's first written to the *sharing-root* folder in one of the locations from the override file: ```cs // Write into the specific sub-directory @@ -38,7 +38,7 @@ await Launcher.OpenAsync(new OpenFileRequest }); ``` -You can verify that the file is being shared correctly if the shared URI excludes the sharing root directory. For example, if you share the file *\/sharing-root/mydata.txt* and the shared URI is `content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/sharing-root/mydata.txt` then the file provider is not using the correct path. If the shared URI is `content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/mydata.txt` then the file provider is using the correct path. +You can verify that the file is being shared correctly if the shared URI excludes the sharing root directory. For example, if you share the file *\/sharing-root/mydata.txt* and the shared URI is `content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/sharing-root/mydata.txt` then the file provider isn't using the correct path. If the shared URI is `content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/mydata.txt` then the file provider is using the correct path. > [!WARNING] -> If when sharing a file you receive an `Java.Lang.IllegalArgumentException`, with a message similar to "Failed to find configured root that contains /data/data/com.companyname.overwritefileproviderpaths/cache/some-non-sharing-path/mydata.txt", you are most likely sharing a file that's outside of the sharing-root. +> When sharing a file, if you receive an `Java.Lang.IllegalArgumentException`, with a message similar to "Failed to find configured root that contains /data/data/com.companyname.overwritefileproviderpaths/cache/some-non-sharing-path/mydata.txt", you are most likely sharing a file that's outside of the sharing-root.