Permission requiired you must approve this premission in app settings:storage

Thanks to Anton Rassadin’s help doc, @RealAero and @ericwa I was able to get saves working with Unreal 5.4.3 on the Meta Oculus Quest 3. Saves weren’t working for me after I’d upgraded to 5.4. I could not save save files on my quest. Here’s all the relevant settings I’m aware of, and how i’ve set them, hopefully this is helpful to see it all in one place.

Build: shipping or development
Android
APK Packaging
Minimum SDK Version 32
Target SDK Version 32
useExternalFilesDir: True

Advanced APK Packaging

Extra Permissions
android.permission.WRITE_EXTERNAL_STORAGE
android.permission.READ_EXTERNAL_STORAGE

Launch Images
Show launch image: True

Android SDK settings:

I was getting this issue from MQDH meta quest developer hub / adb logs.:

java.lang.IllegalArgumentException: Primary directory UnrealGame not allowed for content://media/external_primary/file; allowed directories are [Download, Documents]

Here’s the full log error:

18:17:16.873  2146 19214 E MediaProvider : java.lang.IllegalArgumentException: Primary directory UnrealGame not allowed for content://media/external_primary/file; allowed directories are [Download, Documents]
18:17:16.873  2146 19214 E MediaProvider : at com.android.providers.media.MediaProvider.ensureFileColumns(MediaProvider.java:3370)
18:17:16.873  2146 19214 E MediaProvider : at com.android.providers.media.MediaProvider.ensureUniqueFileColumns(MediaProvider.java:3021)
18:17:16.873  2146 19214 E MediaProvider : at com.android.providers.media.MediaProvider.insertFile(MediaProvider.java:3780)
18:17:16.873  2146 19214 E MediaProvider : at com.android.providers.media.MediaProvider.insertInternal(MediaProvider.java:4364)
18:17:16.873  2146 19214 E MediaProvider : at com.android.providers.media.MediaProvider.insert(MediaProvider.java:4064)
18:17:16.873  2146 19214 E MediaProvider : at com.android.providers.media.MediaProvider.insertFileForFuse(MediaProvider.java:8375)
18:17:16.873  2146 19214 E MediaProvider : at com.android.providers.media.MediaProvider.insertFileIfNecessaryForFuse(MediaProvider.java:8470)
18:17:16.878  2146 10646 W MediaProvider : Forgot to handle a top level directory in getContentUriForFile?

I added one file, and edited another:
image

AndroidSanitizePermissions_UPL.xml:

<root xmlns:android="http://schemas.android.com/apk/res/android">
  <androidManifestUpdates>
	  <loopElements tag="meta-data">
	  <setStringFromAttribute  result="NameTagValue" tag="$" name="android:name"/>
	  <setBoolIsEqual result="UseExternalFilesDirElement" arg1="$S(NameTagValue)" arg2="com.epicgames.unreal.GameActivity.bUseExternalFilesDir"/>
	  <if condition="UseExternalFilesDirElement">
		<true>
		  <removeElement tag="$"/>
		</true>
	  </if>
	</loopElements>

	<addElements tag="application">
	<meta-data android:name="com.epicgames.unreal.GameActivity.bUseExternalFilesDir" android:value="true" />
	</addElements>
  </androidManifestUpdates>
</root>

Then create a link to this in your build script: *the project name in this case is NitroGrid

NitroGrid.Build.cs:

using UnrealBuildTool;

public class NitroGrid : ModuleRules
{
	public NitroGrid(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
	
		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput" });

		PrivateDependencyModuleNames.AddRange(new string[] {  });

		// Uncomment if you are using Slate UI
		// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
		
		// Uncomment if you are using online features
		// PrivateDependencyModuleNames.Add("OnlineSubsystem");

		// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
		
		// Allow saves on android
		if (Target.Platform == UnrealTargetPlatform.Android)
        {
            string manifestFile = System.IO.Path.Combine(ModuleDirectory, "AndroidSanitizePermissions_UPL.xml");
            AdditionalPropertiesForReceipt.Add("AndroidPlugin", manifestFile);
        }
	}
}

After making those changes, saving save objects worked great.

I did not need to request save permissions with this code, possibly because I installed via the .bat file generated with the APK file, which grants permissions. If you’d using a different install method, you may need to request permissions like this:

2 Likes