Write a private access file to another app's files directory - android

The two apps have the same sharedUserId. When I use this code in app1
context.openFileOutput("/data/data/org.me.app2/files/shared-data.dat", MODE_PRIVATE)
I get an exception telling me that the file contains a path separator.
I am trying to write a file from app1 into app2's storage. (I do of course need to make sure that app2's files directory exists first)
Ideally, I would write to a user specific directory instead of an app specific directory, but I do not know if that can be done

First of all, NEVER use a full path to internal storage like /data/data. Let the operating system give you the path (for example, via Context.getFilesDir() or Environment.getExternalStorageState()). Don't make assumption on where the data is.
Secondly - you already are doing that! Unlike File, Context.openFileOutput already prepends /data/data/[package] to your path, so you don't need to specify that. Just specify the file name.
If you really feel that it's safe and necessary, and if both apps share the same user ID using android:sharedUserId in the manifest, you can get a context of the other app by using Context.createPackageContext() and use CONTEXT_RESTRICTED, then use openFileOutput with only the file name.

Open a FileOutputStream of the needed file, relative to this path:
String filePath = getPackageManager().
getPackageInfo("com.your2ndApp.package", 0).
applicationInfo.dataDir;

Since this is months old I assume you've already solved your problem, but I'll contribute anyway.
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.

You should not be overwriting other applications files. That said you have two solutions
Use public external storage (like the SD card) to share the file between the apps.
If the other app is not yours then you can't write to its /data directory, without root that is. Anything is possible with root, just don't expect your users to all have root access.
Edit: Developer owns both applications
Thanks for Roman Kurik for pointing this out. A link to his post on SO
From the android docs
android:sharedUserId
The name of a Linux user ID that will
be shared with other applications. By
default, Android assigns each
application its own unique user ID.
However, if this attribute is set to
the same value for two or more
applications, they will all share the
same ID — provided that they are also
signed by the same certificate.
Application with the same user ID can
access each other's data and, if
desired, run in the same process.
So this is exactly the way user id's work in linux, essentially you are the owner of both and have read/write access to both.

Related

Android.OS.Environment.GetExternalStoragePublicDirectory is deprecated. Alternative?

I'm currently building a .Net Maui App that is only targeting android. I need to save some data in the public external storage that I can access and copy to my PC and that persists even if I uninstall the app. I choose the Documents directory.
The following does the job:
string dir = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDocuments).AbsolutePath
But I get a warning from my IDE that GetExternalStoragePublicDirectory is deprecated.
I've found several postings that
Android.App.Application.Context.GetExternalFilesDir(Android.OS.Environment.DirectoryDocuments)
will also work, but that does not return the same result.
The deprecated method returns
/storage/emulated/0/Documents
while the other one returns
/storage/emulated/0/Android/data/com.companyname.myappname/files/Documents
Hence the appname is in the path, this is not persistent in case the app is uninstalled. So what is the correct way to get the public external documents directory?
I had done a sample to test the MediaStore and the Context api. According to your description, you want to save some data to the file in the Document folder.
For the Context
All the files or directories you get from it belong to the application, when user uninstall the app from the devices, all of these will be deleted from the device. So this doesn't meet the requirement.
For the MediaStore
This api can be used to create file in the public folder, but the file can just be accessed by the app which create it. When user uninstall the application, you can't access it either.
And the google suggest the developer use the ACTION_CREATE_DOCUMENT to let the user create a file by himself and then user can open it by the ACTION_OPEN_DOCUMENT.
So even though the Android.OS.Environment.GetExternalStoragePublicDirectory method has marked as deprecated, it is the easier way to get the result you want.
For more information, you can check this case and the official document.

The only way to get a File object by letting the user choose a file is MANAGE_EXTERNAL_STORAGE?

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.

Application A accessing a textfile stored in Application B's Internal Memory

My app (App A) need to write a textfile which will be stored under another app's(App B) internal memory. Any idea how to implement this?
I have tried the following:
Under both the AndroidManifest.xml file, I have specified the same android:sharedUserId.
From App A, I used:
filePath = getPackageManager().getPackageInfo("packagename of App B", 0).applicationInfo.dataDir;
to get the path and I have confirmed that the path is correct for App B's internal memory.
But I am getting "java.io.IOException: Permission Denied".
Any idea where I have made mistake?
According to official documentation here,
Technically, another app can read your internal files if you set the file mode to be readable. However, the other app would also need to know your app package name and file names.
It's a do-able thing.
So, create a File object with your filepath and call setReadable on it.
Also, check you're not creating the file with MODE_PRIVATE.

Android: How to copy a SQLite database from one application to another

I have a lite version of an application that uses a SQLite database. I want to copy that database over to the full version of the application when the user installs the full version.
I have written some code to perform the file copy, but the lite database file always comes up as unreadable. The file is there and I can point to it, but I can't read it to perform the copy.
In the Android documentation, we read:
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
(nor can the user).
Note the words, "by default".
Is there a way that I can override that default and make the SQLite file readable by my other application?
Thank you.
I believe you have 2 options.
Set the sql database to be world readable on creation. You can do this by setting the appropriate mode parameter in the call to openFileOutput() or openOrCreateDatabase().
Set the sharedUserId attribute in the manifest of both of your applications so that they have the same user ID. This treats both applications as the same user, giving both applications access to the same private set of files.

protect a file

In one android application, I created a database file in data/data/com.rams/databases/dbfilename.
If I created a second application with the same package name (com.rams) and I access the database file created with my first application, the second application is able to access the database contents.
How can I secure the database file created with my first application?
Almost without exception, and regardless of language or platform, there will always be a way for an application running with the relevant credentials, an application running within a certain environment or a user with the relevant credentials to access and read a given file.
You should assume it will always be possible to access a given file in ways you never intended.
Instead of trying to manage access to the file, try to manage comprehension of the file contents. In other words, it won't matter if everything and everyone can access and read a file if the contents are protected such that only an allowed application can understand the contents.
Or, more simply, look into encrypting the file contents if you need to absolutely ensure that nothing other than allowed applications can make sense of what is in a given file.

Categories

Resources