Android Shared Internal Storage - android

Is there such thing on any Android based device as shared internal storage? I know you can use the SDCard as a shared file location between applications, but for a project I'm working on we dont want the ability for a SD Card to go missing (sensitive material).
The problem is as follows. App1 allows a user to browse (for example) some word documents and download them to the proposed shared storage, the user can then pass this file to Documents 2 Go editing them and saving the change. App 1 then allows an upload of the changed file.
I don't fancy building a document editor word/excel directly into app, unless thats easy?
EDIT:
The second app is "Documents 2 Go" I won't be able to edit its AndroidManifest

I faced a similar situation now for txt files and did this.
File downloadedFile= new File( context.getFilesDir(), "simple.txt" );
downloadedFile.setReadable( true, false );
downloadedFile.setWritable( true, false ); //set read/write for others
Uri downloadFileUri = Uri.fromFile( downloadedFile );
Intent intentToEditFile = new Intent( Intent.ACTION_EDIT );
intentToEditFile.setDataAndType( downloadFileUri, "text/plain" );
context.startActivity( intentToEditFile );
Now the 'Document 2 Go' editor will be launched to edit the file and
will be able to edit simple.txt
Note 1: Uri should be created from same file object that was set with
setReadable()/setWritable.
Note 2: Read/Write permission for other users might not be reflected in file
system. Some times I cannot see rw-rw-rw- in adb shell

I believe ContentProviders is your solution. Its the Android recommended method for sharing application data between different apps.

https://stackoverflow.com/a/6708681/804447
Sharing data between apps is what ContentProviders are for. Assuming that you know how to write a ContentProvider and access it, you can access files via ParcelFileDescriptor, which includes constants for the mode in which you create the files.
What you need now is to limit access so that not everybody can read the files through the content provider, and you do that via android permissions. In the manifest of one your apps, the one that will host the files and the content provider, write something like this:
<permission android:name="com.example.android.provider.ACCESS" android:protectionLevel="signature"/>
and in both apps add this:
<uses-permission android:name="com.example.android.provider.ACCESS" />
by using protectionLevel="signature", only apps signed by you can access your content provider, and thus your files.

Related

Kotlin File(path).walkTopDown() does not find files

I'm trying to read a given directory recursively and find all folders and files. The directory and subdirectory is found, but no files.
The storage structure on my virtual device:
The code used to "walk through the given directory"
import android.util.Log
import java.io.File
// path = "/storage/1B10-1D17/ReadTest/"
class FileHelper {
fun walkTest(path: String){
File(path).walkTopDown().forEach {
// println(it)
Log.e("walkTest extension", it.extension)
if(it.isDirectory){
Log.e("walkTest", "Directory: ${it.name}")
}else{
Log.e("walkTest", "File: ${it.name}")
}
}
}
}
This will output the following:
E/walkTest extension:
E/walkTest: Directory: ReadTest
E/walkTest extension:
E/walkTest: Directory: SubFolder
In my android manifest file I have the following line and the permission is granted:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
What is the flaw in my code, how can I find the three pdf files stored in these directories?
Update 1/2
As can be seen in the screenshot below, the media access is granted:
If I run the following command (as suggested by dan1st), I get the following output, which shows that pdf file in place:
> adb shell ls /storage/1B10-1D17/ReadTest/
File_1.pdf
SubFolder
You generally have no access to arbitrary files on removable storage. This is not changed by READ_EXTERNAL_STORAGE.
Your proposed solution is to use MANAGE_EXTERNAL_STORAGE. Technically, this works, and it is a fine solution for personal-use apps. If your intention is to distribute this app, you will need to file paperwork with Google (and perhaps other app stores) justifying your use of this permission. Stores that disagree with your justification may elect to not distribute your app.
Alternatively, you can use the Storage Access Framework. Use ACTION_OPEN_DOCUMENT_TREE / ActivityResultContracts.OpenDocumentTree and let the user decide where on the user's device (or in the user's cloud storage) the user wants your app to access the user's content. You can then use DocumentFile to walk the document tree to find sub-trees and documents.

Accessing external storage in Android API 29

I'm trying to achieve some clean up tools. More and more manufacturers have forbidden rooting devices due to some "security reason", it's forbidden NOT to request for unlock.
After API 28, This code will make error:
ActivityCompat.requestPermissions(this, new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
}, 1); // Request permission or not, Will got same result
File rootFolder = Environment.getExternalStorageDirectory(); // That is working fine
rootFolder.listFiles(); // That will return null
Sure, I can use this:
android:requestLegacyExternalStorage="true"
But I belive that will be killed in future.
So, Any elegant way to manage SDCard?
On Android 10 Environment.getExternalStorageDirectory() and Environment.getExternalStoragePublicDirectory() will return storage paths but paths are not readable or writable.
For Android 10 you can continue to use paths provided by Environment.getExternalStorageDirectory() and Environment.getExternalStoragePublicDirectory() if you add android:requestLegacyExternalStorage="true" to application tag in manifest file. At runtime your app can call Environment.isExternalStorageLegacy() to check if the request has been done.
Another (not known) possibility (only for Android 10) is to add <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> to manifest file.
The user has to go to the advanced settings of the app and enable from Advanced settings Install unknown apps | Allow from this source.
The nice thing with this is that the user can switch the access rights. You can make it easier for the user if you implement an intent for
Settings.ACTION_APPLICATION_DETAILS_SETTINGS where he can change the settings.
A funny thing is that Environment.isExternalStorageLegacy() returns true then too.
Compiling for Android 11 both options do not work on an Android 11 device. (But they continue to work for Android 10 devices). The paths of Environment.getExternalStorageDirectory() and Environment.getExternalStoragePublicDirectory() are usable again in read mode and very often in write mode too. And this is great as one can simply list the contents of directories like Download or Pictures or DCIM/Camera again using the File class.
But adding <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> to manifest file and implementing an intent for
Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION will give your app read/write access for all files even on removable micro sd card.
(Finally you can remove the google ban not being able to read/write your own micro sd card on your own Android device using your own app).
Environment.isExternalStorageManager() can be used to check if the permission is on/off.
As long as you do not try to upload your app to the play store you are fine.
use android:requestLegacyExternalStorage="true" in your Manifest below <application

How to resolve Android 10's Storage Mess

As Android is very inconsistent between different major Versions regarding File access, I feel a bit lost.
I try to describe the problem as easy as possible:
My Company uses a commercial native DRM to protect other native library's we provide. We have a Licensing App, which invoked some Voodoo to end up with Licensing files in say /sdcard/companyname/LicenseContainer/. Other protected Apps looked at this directory in native code, checking if the user has a valid License.
The Android 10 update however, invalidated this workflow completely as it only provides scoped storage access. We could do a workaround using Storage Manager to grant access, which is unfortunately also deprecated now.
So my Question is now:
How can one App save files to a location on /sdcard/FOLDER which are
not deleted on App deletion
Accessible in native code by other apps
I'm a bit overwhelmed with all the possible solutions (SAF, FileProvider, etc), which invoke often that one app grants permissions to the other. But the files should be accessible without an installed first app who put it there.
I know there must be a solution, as recent FileManagers (i.e. Files by Google) get access to the whole /sdcard/ directory.
Whats the easiest, future-proof route to go here without invoking "hacks" like android:requestLegacyExternalStorage="true"
You may ask the user to give you access to any file or directory, including the root of internal storage or external SD card. You can make this access permanent for your app, be able to read/write files anywhere with the Scoped Storage API afterwards, until the app is uninstalled or reset.
Then, if you need to read or write a file in native C/C++ code, you may get Linux file descriptor (int number) of the file and pass it to native code to use with fdopen() call for example.
Here is a Java code snippet to get a file descriptor form a single file Uri (which in string form is like content://...)
ParcelFileDescriptor parcelFileDescriptor =
getContentResolver().openFileDescriptor(uri, "r"); // gets FileNotFoundException here, if file we used to have was deleted
int fd = parcelFileDescriptor.getFd(); // detachFd() if we want to close in native code
If you have source code for your native libraries, or can call them with C FILE* - it will work fine. The only problem is when you don't have the source code and they expect a file path/name. * UPDATE *: it is still possible to use the path/file name strings to pass to C/C++ functions that expect a file name. Simply instead of the "real path/file name", create a name to symbolic link like this:
// fd is file descriptor obtained in Java code above
char fileName[32];
sprintf(fileName, "/proc/self/fd/%d", fd);
// The above fileName can be passed to C/C++ functions that expect a file name.
// They can read or write to it, depending on permissions to fd given in Java,
// but I guess C/C++ code can not create a new file. Someone correct me,
// if I'm mistaken here.
However, at this time I'm not sure that when you create a file in a directory beyond the app "sandbox" in that way, if the system will delete this file too after uninstall... Would need to write a quick test on Android 10 to find out, and then we still won't know if Google won't change this behavior in future.
If you want to save files in shared storage (where it can be accessed by users & other apps) you need to use
for Media Files (Images, Videos, Audio, Downloads) use MediaStore
for Documents and Other Files use Storage Access Framework (this is simply a system file picker)
For instance you can use the following snippet to save a pdf file using Storage Access Framework
const val CREATE_FILE = 1
private fun createFile(pickerInitialUri: Uri) {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/pdf"
putExtra(Intent.EXTRA_TITLE, "invoice.pdf")
// Optionally, specify a URI for the directory that should be opened in
// the system file picker before your app creates the document.
putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
}
startActivityForResult(intent, CREATE_FILE)
}
After the user has picked a directory we still need to handle the result Uri in on onActivityResult method.
override fun onActivityResult(
requestCode: Int, resultCode: Int, resultData: Intent?) {
if (requestCode == CREATE_FILE && resultCode == Activity.RESULT_OK) {
// The result data contains a URI for directory that
// the user selected.
resultData?.data?.also { uri ->
// save your data using the `uri` path
}
}
}
You can read about this in more detail on the following blogpost
https://androidexplained.github.io/android/android11/scoped-storage/2020/09/29/file-saving-android-11.html

Xamarin SqlLite database in Android Internal shared storage

My application will be used offline and I plan daily pull/push synchronizations via USB cable. My users are in a very primitive situation: no wifi, and no cell phone towers. My question is not about synchronization, but rather just getting access to the data so that I can synchronize.
I connect the cable, select USB for file transfer, and I can see Internal Shared Storage. But I cannot find my SqlLite database anywhere. I have tried using these paths for the database:
Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData);
Environment.GetFolderPath(Environment.SpecialFolder.Personal);
Android.App.Application.Context.FilesDir.AbsolutePath;
The app works fine with any of those paths, the data is stored and retrieved, but I cannot see the database from my PC.
I have also tried this but it blows up:
Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).AbsolutePath;
I have tried debugging with Xamarin Live but then I get this error:
"You need to call SQLitePCL.raw.SetProvider();"
I have tried adding console.writeline and Log.Error to add in some diagnostics but I can't find any log files in Internal Shared Storage.
I have WRITE_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions in the manifest; even tho I think that is not necessary.
If I could store files in Internal Shared Storage then I could put the database there and access it to synchronize. And I could create a simple text logging facility to write a text log to the same place.
I have rarely asked for help in 40 years but I've been at this for days. Thanks!
To get files onto Internal Shared Storage accessible via USB took 3 steps:
1: Get runtime permissions
ActivityCompat.RequestPermissions(activity, new String[] { Manifest.Permission.WriteExternalStorage, Manifest.Permission.ReadExternalStorage }, 1);
2) use this path:
string extPath = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
extPath = Path.Combine(extPath, "RtTrace.txt");
File.AppendAllText(extPath, "new content" + System.Environment.NewLine);
3) Media scan the resulting file to make it visible:
MediaScannerConnection.ScanFile(Android.App.Application.Context, new String[] { extPath }, null, null);
The path on Environment.SpecialFolder.Personal refers to a private area where only the app (and the OS itself when you clear data from the app, for example) have access. I don't know about ApplicationData.
You can easily copy your app file (the protected one) to a public folder, like Downloads or create a new folder MyAppDirectory at the public storage space, that will allow access from other devices. Then, you can clear local data that you don't need anymore (after the sync process).
To create a new public folder on Android:
var folder = System.IO.Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, "MyAppDirectory");
System.IO.Directory.CreateDirectory(folder);
You'll get this:
Then, copy the file:
var newFile = Path.Combine(folder, "MySharedFile"); // The database, xml, json, text or any file you want to share
var sourceFullName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal),"MyOriginalFile");
System.IO.File.Copy(sourceFullName, newFile, true /* overwrite */);
I hope it helps.
SQLite db is stored in application private memory and can't be accessed over USB, try exporting contents of your DB to an external file in your internal memory or External memory to be able to access it via a USB chord.

how do I open a newly written file from application storage directory in as3 / flex mobile?

Im working on an app (flex 4.12 sdk, using flashbuilder 4.5, creating an app for ios and android, testing on an android htc one primarily)... and am using the camera to capture a file... Im then saving that image to the application storage directory, and I want to open the image in the default web browser or trigger a native dialog (android users) to choose the web browser of their choice... how it opens isnt really important right now -- Im mainly trying to just 'access' it with the device and 'load' it outside my air app...
heres the code I have:
var fs2 : FileStream = new FileStream();
fs2.addEventListener(Event.CLOSE, fileCompleteHandler);
var targetFile : File = File.applicationStorageDirectory.resolvePath("test.jpg");
fs2.openAsync(targetFile, FileMode.WRITE);
fs2.writeBytes(myBMDByteArray,0,myBMDByteArray.length);
fs2.close();
and for the event listener that detects the close of the newly created file:
function fileCompleteHandler(e:Event):void {
trace('File saved.');
trace('exists? ' + targetFile.exists);
trace('the url: ' + targetFile.url);
trace('path: ' + targetFile.nativePath);
navigateToURL(new URLRequest(targetFile.url));
}
I get the following info back from this listener
File saved.
exists? true
the url: app-storage:/test.jpg
path: /data/data/air.com.xxxxx.apptesting.debug/com.xxxxx.apptesting.debug/Local Store/test.jpg
... and problem is that navigateToURL cant access the location where the file is stored (the protocol shows in browser as file:///data/data/air.com/xxx... )
how can I use navigateToURL to get access to this newly created file in the web browser or whatever native application the device associates with the file (its a .JPG file)? I also have had success in adding the newly created image to the camera roll but couldnt figure out how to then open that newly saved image in the native camera roll or whatever app the device chooses or presents to the user for the .jpg format.
I can show the user the image INSIDE my app by referencing the bitmap data fine, I just want to give the user access to the physical file that Im creating on their device.
I even have had success in posting (via urlLoader) the bitmap data as base64 encoding and then creating a file on the server side and loading that url but the encoding and trip to and from the server to give the user the image adds a lot of overhead and it takes a little too long and I'd like to avoid that elongated process.
Thanks for any help anyone can provide - let me know if I need to be more specific in any of this.
Solved the issue... I was able to store / write my file in the documentsDirectory using:
var targetFile : File = File.documentsDirectory.resolvePath('test.jpg');
and then
navigateToURL(new URLRequest(targetFile.url));
And this works fine now. Hopefully it helps someone else! Seems that the storage directory SHOULD work but up until now I've only written to and read files stored there... maybe to open the files one HAS to copy it to a 'safe' location in the filesystem (i.e. sd card?)... will move on to test in ios Now - hope all works well in that OS. Thanks all who chimed in on this.
My first hunch is that you need to specify the proper user-permissions in your application descriptor so you can use the openWith functionality with content from your application.
Remember that you need to specify this for IOS and Android specifically.
On your application.xml you need this permissions set inside android > manifestAdditions > manifest:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
With this permissions you can save files to applicationStorageDirectory:
const FILE_LOADER:URLLoader = new URLLoader();
FILE_LOADER.addEventListener(Event.COMPLETE, onTempFileComplete);
FILE_LOADER.dataFormat = URLLoaderDataFormat.BINARY;
FILE_LOADER.load(new URLRequest(BASE_URL + filePath));
The applicationStorageDirectory can only be accessed by the application it belongs too when using Android or iOS. navigateToURL() hands over your request to the default browser, which cannot access said directory.
documentsDirectory is a public directory in Android, but not in iOS. So it cannot be used for both platforms. Unfortunately none of the pre-compiled file paths File has point to a public directory in iOS. You can find a table explaining all the pre-compiled paths here

Categories

Resources