Weird createInputStream behaviour (Android)

As an aside I wrote these notes from a video of Google’s;

Android 11 September 8, 2020

  • Enabled File Paths APIs
  • Bulk media modifications APIs
  • All Files Access ← broadly read file system
  • Private app storage

Using File Paths

  • Allow devs to use file apis while keeping the new security features.
    • “FSE” File System - User Space; re-enabled File API with scoped storage
    • Under the hood file access is delegated to the MediaStore API
    • File path API is a convenience API to the MediaStore
  • Alternative to MediaStore APIs
  • Better compatibility
  • Java Files API
  • Native Libraries (C, C++)
  • Same scoped storage access
  • Immediately indexed by MediaStore (old way you had to scan the file)
  • MediaStore holds file path in ‘data’ column

Modifying Media

  • Can batch modify after confirmation from user
  • createWriteRquest()
  • createReadRequest()
  • createTrashRequest() (gives the user a chance to recover file later), deleted by OS after 30 days.
    • Can be untrashed by the file owner or user consent before 30 days
  • createFavoriteRequest()
    • works across apps

All Files Access

  • MANAGE_EXTERNAL_STORAGE
  • Write access to all shared files.
  • Special app access.
  • Google Play manual review.

Tips on Migrating

  • Legacy storage flag. (all files access/file paths)
    • To ensure correct behavior for users of Android 10
  • Storage access framework
    • Custom picking experience for custom files like documents. (does not require permissions)
  • Modifying media.
    • modifying media files you do not own, requires user consent (MediaStore consent APIs)
  • Top level directories (don’t save in custom top level directories)
    • requires the SAF which “is not the best experience for users”
    • App uninstalled and reinstalled, the app would loose access to the files
    • Use organized media collections or private storage instead
  • Content Providers
    • share data with other apps
    • most secure way to share files
1 Like

Oh my god this stuff is such a nightmare. That last post of yours @TeotiGraphix made my head spin!

With the latest updates I’ve made on my side (see all posts above) I now have file access working on android 11 phones (my Samsung note 20 ultra) and earlier android versions too (my app supports Oreo and above).

Note that the files my app stores are currently in a hidden internal folder at this point in time. However, I will again attempt to switch to a public folder after having made the recent changes I’ve made to file permission stuff. Who knows, it might magically work in a public folder now.

Hi Pete,

I sent a more detailed description to you via your website, but with my app the ‘allow management of all files’ is indeed set after opening my app for the first time and allowing storage permissions via the popup that appears the first time I load my app.

To answer your question. I just tried uninstalling then reinstalling my app. I found that if I don’t open the app it is denied permission, but after opening my app once and allowing media permissions via the popup, it changes to ‘allow management of all files’ thereon after.

Hope this is helpful

Yes, the safe private storage. It’s the public “root” that will be no longer accessible to normal apps.

The MANAGE_EXTERNAL_STORAGE permission is from my knowledge a verified by Google type thing and you have to really show you need it(they have said…). This in essence gives back old functionality of the File reading system down to the public root that users can see on other things like a PC.

FileExplorers etc would need this type of permission, audio apps seem to fit as well, after things move ahead a bit I will probably try Google with the MANAGE_EXTERNAL_STORAGE as well.

My apps currently don’t use JUCE. So I have implemented the Storage Access Framework and Content Providers for my use case. But storing public projects was what I did for years, and fast access to other audio directories.

If it turns out that I have to stick with the hidden internal folder in order to get all this stuff to work, I’ll just stick with that. It’s just a shame to have to limit the user experience like that. As I said, things are now finally working on android 11 phones and below so I’m pretty relieved.

2 Likes

I have a couple small apps I am making and just using private file access to start with as you are.

I am waiting to update my existing ones until I am completely sure what is going on with this access all permission. Plus I had written a custom file explorer in my apps for the projects, custom preview audio, notes, preview wavs etc., losing that navigation changes everything and I have been hesitant to change this. I might have a shot at getting that permission so my apps can keep working like they currently do, since they have been on Play for a while now.

Well if you find any other revelations don’t hesitate to post, I will do the same.

1 Like

This is certainly a pain in the proverbial.

I’m pretty sure that the solution we have in place now for Wotja, is going to be broken in future by some future Android update. And that, ultimately, is the frustration of developing for Android - it is like building a house on sand.

Best wishes, Pete

@peteatjuce @TeotiGraphix @jrlanglois

Last night I tried switching to a public folder (Documents/Spacecraft) to store the assets of my app and, to my surprise, it worked. This is with all of the implementations covered previously in this thread.

So, I seem to have stumbled across a solution for Android file management that works, at least for now until google screws things up again like it has for you guys in the past.

Note that upon first app startup following installation, when I write the files to disk, I need to enclose those methods within a call to RuntimePermissions::request as shown in the original post.

One of the issues is that Documents might not exist - I’d also recommend looking for Music / Download as alternative fallbacks!

Pete

Ah, I didn’t realise that was a thing.

In that case, I’ll include an option in global settings to allow the user to select either documents or music as the location, that will require a restart of the app. There won’t be the need to copy any files in this case as they won’t have been able to have created any at that point.

Thanks for the advice Pete

Yeah haha I forgot about the Downloads, what a hack on Googles part, “yes, your projects are saved in the Downloads folder”. :joy:

Just to note that I ended-up reworking Wotja’s interaction with the storage permissions, and touch wood it is pretty smooth now, and working with the latest Android.

When the app starts, it checks to see if it can write a temporary file to public storage. If that fails, it presents an alert telling the user what is going on, and (once alert dismissed) takes the user to the closest possible system Activity to review permissions. When the user returns from that system activity, the next time Wotja tries to write, it either succeed in writing to public storage, or will stick to private storage until the user changes that setting.

If you restart the app and still use private storage, the alert will display itself and the process repeats…

Pete

1 Like

For some reason I find this confusing, but the folder is actually called Download rather than Downloads. :slight_smile:

Pete

1 Like

Well, you think that is confusing, check out this change in the Android framework. !!! :joy: :rofl:

https://developer.android.com/sdk/api_diff/s-dp3-incr/changes/java.lang.Deprecated

2 Likes

You’re right. That is confusing :upside_down_face:

Pete

Oddly enough, as I’ve started getting an Android build of an app I do I’ve also bumped the nullptr for the non-empty url result.

This is for importing a file. and since I barely have Android devices all is in the emulators.
I’ve also had to modify the Manifest with:

android:requestLegacyExternalStorage="true".

Is there a simple way doing that within projucer or I need to patch it :frowning:

Anyone I know there’s a lot on the JUCE team plate but getting broken Android file support even before adapting to API 30, would be great.

Another oddity maybe someone has some context,
Since I use the emualtors.
API 30 works as expected after the requestLegacyExternalStorage, but API25 while working,
The native file browser won’t show up my filtered type (or even .). This is also for presets but also for plain wave files…

1 Like

I long ago ended-up editing all my project files by hand, for all platforms (iOS, macOS, Windows, Android); there are too many customisations that I wasn’t able to deal with using the Juce Project generators.

Hoping this helps,

Pete

If you’re targeting cross platform with JUCE this could easily get out of control.
Sound’s like you should try migrating to cmake which gives much granular control without tidious fix-up for each exporter.

Anyway the Android File System/JUCE itself seems to require some maintenance.
Mostly due to Android team trying to fix problems by branching to new ones…
Hoping device manufacturers implement the right behavior for us :slight_smile:

Also there’s no way to manually set mime for chooser so if JUCE might simply not add your file filter if it’s unknown

Bumping this hoping JUCE team would be convinced to add this required flag to jucer exporter.
:slight_smile:

I’m struggling to reproduce this, here are the steps I’ve tried:

  • Open the DemoRunner project in the Projucer and set the “Target SDK Version” field to 30
  • Save and open in Android Studio and build for an AVD running API 30
  • Open the AudioPlaybackDemo and attempt to load an audio file

File::createInputStream() succeeds and the file is loaded correctly. I’ve also tried saving and loading files in the DialogsDemo and this is working as expected too. Are there any other steps I need to take to reproduce this behaviour?