We are implementing a backup/restore system for the app. We used Google Drive API as documented in the Android Guide
We also store image URIs in the database. In the restoring process, we get URIs but lost permission to reach images when we re-install the app. We have the following exception.
Failed query: java.lang.SecurityException: Permission Denial: opening
provider ... requires that you obtain access using
ACTION_OPEN_DOCUMENT or related APIs
We get image URI via "ACTION_OPEN_DOCUMENT" but can't persist it after deleting/install the application. What are the related APIs? Do we need to move images in an app-specific folder and backup images itself also?
Thank you for reading this, any help is appreciated
What are the related APIs?
ACTION_OPEN_DOCUMENT_TREE and ACTION_CREATE_DOCUMENT are probably what they are referring to.
Do we need to move images in an app-specific folder and backup images itself also?
We cannot really answer that — that is a business decision as much as a technical one. However, if your app is uninstalled and reinstalled:
You lose access to content identified by Uri values that you obtained from the Storage Access Framework
On Android 10+, you lose access to any files that your older app installation created that survived the uninstall process (e.g., they were in a shared collection)
How you work around that is up to you. In addition to your proposal, you could:
Have all the images be stored in a single directory tree (obtained via ACTION_OPEN_DOCUMENT_TREE), store relative paths in your database, and request access to that tree again after your app is restored
Remove all of the Uri values from the database before backing it up and live without access to those images if the database is restored
Store the image data in the database itself (typically not recommended but technically possible)
Related
As we all know starting Android Q (API=29) introduced new Scoped Storage concept, which basically prevents access to external files/dirs, except media files. Namely:
Programmer can use internal/app specific storage (no limits) - getFilesDir()
The only external storage can be accessed is via getExternalFilesDir(), but during app uninstall data will be deleted
There's option to access external storage via MediaStore, but it works only for media files
App still can read any external file via: getContentResolver().openInputStream(uri)
In my case I have app, which stores user data in SQLite database in app specific directory, database size can be up-to several gigs.
In all previous versions of my app users used to backup data to external storage/SD/USB and transfer those data to their new device, so they could continue to use data (it's encrypted database containing any kind of private data, including audio/video/documents and so on).
Question: how can I backup this database? I couldn't manage to find appropriate way to save/backup data to any external storage...
P.S. Android autobackup service is unworkable due to the size of database.
Android documentation mentions this:
External storage directories: These directories include both a dedicated location for storing persistent files, and another location for storing cache data. Although it's possible for another app to access these directories if that app has the proper permissions, the files stored in these directories are meant for use only by your app. If you specifically intend to create files that other apps should be able to access, your app should store these files in the shared storage part of external storage instead
How do I make sure that other apps don't access the external storage? Also, is it true across all Android versions?
You need to create an abstraction using Content Provider.
"A content provider presents data to external applications as one or more tables that are similar to the tables found in a relational database. A row represents an instance of some type of data the provider collects, and each column in the row represents an individual piece of data collected for an instance."
Refer to the link above for documentation.
So basically, my app receives external content:// uris exposed with android FileProvider. As android documentation states:
Permissions granted in an Intent remain in effect while the stack of
the receiving Activity is active. When the stack finishes, the
permissions are automatically removed.
If I want shared resource to be available for my users across multiple app launches I have to copy all it's content to my app's own storage, even if its a big amount of data? (Let's say few gigabytes of photos)
Or do I have some other options to hold permanent access on those uris?
If I want shared resource to be available for my users across multiple app launches I have to copy all it's content to my app's own storage, even if its a big amount of data?
Yes.
Or do I have some other options to hold permanent access on those uris?
No. The only thing that gives you semi-permanent access is if you are using ACTION_OPEN_DOCUMENT, then use takePersistableUriPermissions() on a ContentResolver to request long-term access to the content identified by the Uri that you get back.
Alternatively — if you are controlling both sides of this data exchange — do something else for passing the content references. For example, if the content is on external storage, just pass the filesystem path, and ensure that both parties have the appropriate permissions.
I am thinking regarding the future options of my app and I am thinking of the idea of backing-up the data from the application's Database and also sharing that data with another phone, say via e-mail, messaging, Bluetooth, you name it, but basically saving it as a file and opening it from the other phone and having the same values on both phones.
What would be the best approach for such an Android application?
Would Content Providers accomplish exactly this or are they concerned with sharing data only between different Apps? Thanks!
I believe it is possible ,
If you read the documentation about the internal storage here, It mentions
You can save files directly on the device's internal storage. By default, files saved to the internal storage are private to your application and other applications cannot access them
So i believe you can copy the whole sqlite DB file to some temp location then share that file via BT or email or any other sharing option .
But DO NOTE, that the same application package can only access the file if you want perhaps another application to use the db then u need to set the SharedUserId , as mentioned here
Content Providers are generally only for sharing your app's data to other apps.
Content providers are the standard interface that connects data in one process with code running in another process.
I want to send images created using my app using a send intent.
I looked at many examples of such intents and noticed that they all used the external storage to store the temporary image files that will be sent using the intent.
Is there any specific reason why the external storage is being used?
It looks to me like a dangerous choice since the user wouldn't be able to post images to the web if the external storage is not mounted, which feels rather strange.
Also, where would you save your app's working data? In onPause(), I am saving the current image the user is working on in the internal storage, but would you recommend storing it in external storage instead?
This is common because images can be fairly large. In some devices internal storage is somewhat precious (hence the popularity of Apps2SD). Additionally, sometimes you might want these images accessible to the user outside of your app. If it's truly just temporary, you should consider using getCacheDir() or getExternalCacheDir().
See this doc for info on storage. There's code in that doc to see if external storage is available if you want to use internal and fallback to external.
Oh, the main reason though in your scenario is simply that your chunk of internal storage is only accessible to your app. If the send intent is to a component that is not your app, it won't be able to access your area of internal storage.
The reason is that there is a limit to IPC data in Android -- around 1MB. That means that if you put more than that in your intent's extras, you will get an error when you try to start an activity using this intent. So the only way to pass images via intents is to pass a pointer (URL, filename) to the actual image. Since private files are only visible to your application you have to either store to external (shared) storage or pass a content provider URI (you need to write the content provider, of course). Shared storage is the easier way.
An Image is in a byte array format. when you are sending directly, it might be possible that it takes time ( i.e. if you are sending on remote server ). Sometimes application may get hung. So to avoid such sequential programming, developer uses parallel code, here one code save the image in storage device and another works for uploading part.
One More Benefit is that direct sending can causes in data loss while saving on external storage prevents such cases.