In my application I allow users to share files with external apps by using content provider. My files are encrypted so before I can share one of those files, I need to decrypt it and store it in application internal storage. After that I implemented custom content provider that can point to the file by using a given URL and return ParcelFileDescriptor instance.
For security issues I would like to remove the file from internal storage after external application has used it, or user canceled operation.
EDIT: The external application that I am sharing file with is a 3rd party application.
So my question is:
When should I remove the file, or do you know of any event that I can listen to or a handler that I can attach to the file?
Thank you for all you responses.
Side question would be:
Do you know of a way where I wouldn't have to store the file on internal storage, but rather send it directly as a stream to the external app?
The answer to my question is to use pipes ParcelFileDescriptor.
Code is described in The Busy Coder's Guide to Android Development from CommonsWare.
The solution is based on:
https://github.com/nandeeshwar/Pfd-Create-Pipe
Actual solution that I managed to implement is:
https://github.com/commonsguy/cw-omnibus/tree/master/ContentProvider/Pipe
If it is your external application, you could leave an encrypted file and pass the key in an Intent. If it is a 3-rd party external application, you have to find out what that application can accept.
You might also read about services -- maybe they provide what you want.
As to "when", I'd suggest launching a clean-up task in onResume(). (Unless you decide to use a service.)
Related
I want to show a file browser to the user, let him choose an arbitrary file he wants, and read it using the ZipFile class. The class requires a File object, not an InputStream.
If my research is correct, the in-built file choosers (ACTION_OPEN_DOCUMENT, etc) only provide a stream, not a File, right? Even if I get the file name from the chooser and have acquired READ_EXTERNAL_STORAGE from the user, it seems that in recent Android versions, the API only allows "only media" and reading files of other types seems to fail.
If I have also acquired MANAGE_EXTERNAL_STORAGE, then it works, but that shows a scary warning like following to the user.
Allow this app to access, modify, and delete files on the device or any connected storage devices? This app may access files without asking you.
That seems to be an unnecessarily broad permission when all I want to is a read-only access to File for a file that the user has explicitly chosen in the file browser. There is no other way than acquiring MANAGE_EXTERNAL_STORAGE, is that right? I want to make it sure, because it sounds weird. Even a very restricted environment like a web browser, the browser does not require any special permission or show any warning for reading a file that the user has explicitly selected, and the same was true for UWP app.
If my research is correct, the in-built file choosers (ACTION_OPEN_DOCUMENT, etc) only provide a stream, not a File, right?
Correct. After all, the user can choose something that you cannot access via the filesystem, either due to permissions or due to the fact that it simply isn't on the filesystem (e.g., cloud storage, SMB/CIFS file server).
IOW, what you refer to as "file choosers" are not limited to what you seem to be thinking of as files.
If I have also acquired MANAGE_EXTERNAL_STORAGE, then it works
I do not know what "it" is. You cannot hack into Google Drive to get a File object using MANAGE_EXTERNAL_STORAGE, for example. Nor can you get a File object for a document contained as a BLOB in an encrypted database, etc.
There is no other way than acquiring MANAGE_EXTERNAL_STORAGE, is that right?
You cannot do what you want even with MANAGE_EXTERNAL_STORAGE. A Uri is not a file.
Your options are:
Copy the content identified by the Uri to some file that you control (e.g., in getCacheDir()), so you can use ZipFile.
Relax the ZipFile constraint. For example, perhaps you could use ZipInputStream, or find a third-party ZIP reader that supports InputStream (such as this or this). You can get an InputStream on the content identified by the Uri via openInputStream() on ContentResolver.
Relax the "in-built file choosers" constraint, and implement your own "file chooser" with files that your app can read on the filesystem. That way, you are guaranteed that you can use File, since you used File to get to them in the first place.
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)
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.
How can I implement ability to attach files from file system to my application. Can I use some libraries, or I should do it myself programmatically? Or I can implement code of other similar application and use them?
File browser:
Link
A sample code for file browser. Or you can just let user input the location into an EditText and let app remember it.
To use external storage(like SD card), please see here
Remember the file location:
Using Shared Preferences
It is a simpler way to save persistent data than SQLite database
Send it to server:
Link
Here is an example of how to use HTTP POST to send to server
I have a requirement to attach a Zip file to a message in the Android email composer.
The Zip file is being created by my application and stored in the app's private storage area (accessible via getFilesDirectory()).
I am obtaining the URI to this Zip file and appending it to an email intent object; when I launch the email activity I am able see the attachment but my email recipient is not receiving the files,
After doing some research on this, I found that my application data cannot be shared with other app's (in this case the Android Email app).
To solve this problem I would like to implement the recommend solution of using a content provider to share my application data.
First of all, I would like to confirm if this is possible, and if it is could anyone please give me some hints on how to go about doing this. I need to know how to copy my Zip file from the getFilesDirectory() of my application, to the content provider, and also how to attach the content provider URI to the email intent object.
I can't place my zip files into an SD card...
I just want only to store my files in to my device internal storage and attach to the email composer.
By default, a ContentProvider can be accessed by any application on the device. If you are willing for these files to be accessed by any application on the device, create a ContentProvider, with real implementations for getType() and openFile(). Then, the content:// URL should work with the Email app, AFAIK.
Here is a sample project demonstrating a ContentProvider serving up files from local storage, in this case to a WebView.
If all you want is to avoid storage on the external SD card, then you don't need a ContentProvider. You can live with
openFileOutput("yourzipfile.zip", MODE_WORLD_READABLE)
and pass
putExtra(Intent.EXTRA_STREAM, Uri.fromFile(getFileStreamPath("yourzipfile.zip))
along with your ACTION_SEND Intent.
However, the email program will probably set no Content-type.
Also, there's no reliable way to tell when the email app doesn't need your file anymore. This means that you either risk ending up with many files or sending a newer content as the attachment of an older email.
Another issue with this solution is that everyone can read your zip file. This problem would not occur with a ContentProvider solution, where you can grant access permission on a per-Intent basis, i.e. allowing access to one file only for one email Intent.
Matching of the Uri is then done via the URI, which may start with your package name, such as
content://com.yourdomain.yourapp.yourproviderclass/some/path
You may want to look at http://developer.android.com/guide/topics/manifest/provider-element.html