I trying write/read file on sdcard using PhoneGap. Reading Android File System I test cordova.file.dataDirectory, cordova.file.externalApplicationStorageDirectory, cordova.file.externalRootDirectory but this give me access to:
file:///data/user/0//files
file:///storage/emulated/0/Android/data/
file:///storage/emulated/0/
The question, I can read/write this directories but I need leave this file public, out of sandbox.
So, any idea about how do this?
Since Android 4.4, apps are restricted to read-only access outside of their sandbox. The only place the app has write access to is within its sandboxed areas:
in /data/ area, e.g. file:///data/data/your.app.id/
in internal /sdcard area, e.g. file:///storage/emulated/0/Android/data/your.app.id/
in external SD card area (if present) e.g. file:///storage/emulated/1/Android/data/your.app.id/ - note: this is not accessible via cordova-plugin-file
It is not possible for an app to write to other locations outside of these sandboxed areas.
Related
The MediaPlugin library creates files in
storage/emulated/0/Android/data/[app_name]/files/Pictures/18-Feb-19/1550503112_in.jpg
What would I use to get access to the picture in this path later?
I'm trying to avoid using a hard-coded string... I've tried googling it but I'm getting really confused as to how to get this path by using Android predefined values like android.os.environment.datadirectory or System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyPictures); etc.
Android groups the filesystem into two different types of storage:
1 Internal Storage – this is a portion of the file system that can be accessed only by the application or the operating system.
You can access the internal storage using code like this
System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyPictures)
And the path is like this:
/data/user/0/MyApp.Android/files or /data/data/{package name}/files
2 External Storage – this is a partition for the storage of files that is accessible by all apps, the user, and possibly other devices. On some devices, external storage may be removable (such as an SD card).
You can access the external storage using code like this
Android.Content.Context.GetExternalFilesDir(Android.OS.Environment.DirectoryPictures)
And the path is like this
/storage/emulated/0/Android/data/{package name}/files
So you can see the path Media Plugin has created is the external storage path. You can access it using
Android.Content.Context.GetExternalFilesDir(Android.OS.Environment.DirectoryPictures)
I have some downloadable content (e.g.:game character image) in a app, in iOS they are saved to path NSCachesDirectory, which I follows the guidelines of iOS that downloadable content should be saved in NSCachesDirectory, which is a cache directory. I am struggling if I should save the files to getCacheDir() in android side.
At first I think the cache folder in iOS should be equivalent (or similar) to the one in android, but after looking at some doc I doubt if they are functionally identical:
1.iOS suggest some downloadable content to save in cache folder, such content is more persist temporary file in tmp. But in android, the doc says getCacheDir() should store cache files rather than persist files, it makes me suspect android cache folder works more like the tmp folder in iOS
2.I cannot find any size limit guideline on iOS cache file, but android side says it should have reasonable size limit, say 1 MB (and I think my content would be far more than 1 MB, say 50 to 100 MB).
So my question is, are iOS cache folder works functionally equivalent to cache folder in android? Is it correct to use getCacheDir() at android side at the place that I use NSCachesDirectory in iOS side? if not,which path should I use at android side when I use NSCachesDirectory at iOS side?
No, iOS cache folder doesn't works functionally equivalent to cache
folder in android.
For more details of all the data-storage options of Android have a look at official docs here.
In Android for storing large amount of data you can go with either Internal or external data-storage options, Also you can make your data private on Internal or external data-storage options if you need so.
Internal storage
Each application has its own private internal storage to save files. This is the kind of storage to use if the user shouldn’t be able to modify the file from outside your application, and if other application shouldn’t be able to access those files. Since the internal storage is private to your application, the files will be deleted if your application is uninstalled. The internal storage is also where your application is installed by default, so your files will always be available. On some older or cheaper devices the internal storage is quite limited, so you need to be careful about the size of the data you save if you need to support those devices.
You should never hardcode the path to the storage directories, since the directory may changes depending on the version of the Android OS used. Also, Android 4.4 introduces the concept of multiple users : in that case, the internal and external storage depend on the user logged in and the files of the other users will be invisible. Here are some of the methods used to get the paths to the internal storage:
android.content.Context.getFilesDir(): returns a java.io.File object representing the root directory of the internal storage for your application from the current context.
android.content.Context.getDir(String name, Context.MODE_PRIVATE): returns a java.io.File object representing the directory name in the internal storage, creating the directory if it does not exists. The second parameter can also be used to set the directory to MODE_WORLD_READABLE or MODE_WORLD_WRITABLE so it is visible by all the other applications, but this is is risky security-wise and was deprecated in API level 17 (Android 4.2).
android.content.Context.getCacheDir(): returns a java.io.File object representing the internal cache directory for the application. This is mean for small files (the documentation suggests no more that 1MB total) that can be deleted at any time when the system needs more storage. There is no guarantee that the cache will be cleared, so you must also clear those files manually when they are not needed anymore.
As you can see, the files are represented by the File object from the java.io namepace: there is no file object specific to the Android SDK and the standard Java APIs for reading and writing files are used. Also, there is no specific application permission to set in the Android manifest to use the internal storage since it is already private to the application.
External storage
In addition of the internal storage, there is an external storage space shared by all the applications that is kept when your application is uninstalled. This is the storage that is shown when using a file explorer application and when the device is plugged in your computer. It may be implemented as a SD card that can be removed or as a partition of the built-in storage in the device, so your application should be able to work even if the card is removed or changed. To check the current state of the external storage, you can call the getExternalStorageState() method.
On device with many users (starting with Android 4.4), the external storage is specific to the current user and files for other users can’t be accessed. Also, there may be more than one external storage if the device has a built-in external storage which is a partition on the internal memory and a SD card: in that case, the built-in storage is the primary external storage. Reading files from the external storage requires the READ_EXTERNAL_STORAGE permission and writing or reading files requires the WRITE_EXTERNAL_STORAGE permission.
Here are the methods you should use to call to get the directories of the primary external storage:
android.os.Environment.getExternalStorageDirectory(): returns a java.io.File object representing the root directory of the primary external storage of the device that is shared by all applications.
android.os.Environment.getExternalStoragePublicDirectory(): returns a java.io.File object representing a public directory for files of a particular type on the primary external storage of the device. For example, you can get the path to the public music directory by calling Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC) or the public pictures directory by calling Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).
android.content.Context.getExternalFilesDir(): returns a java.io.File representing the root directory of the primary external storage specific to your application, which is under the directory returned by getExternalStorageDirectory(). Unlike the other directories of the external storage, the files you store in that folder will be deleted when your application is uninstalled. So, if you need to store files that are only needed by your application you should use this folder. Also, there is no specific permission needed for the application to read or write to its own external storage starting with Android 4.4, but with older versions your application needs the READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE permission.
android.content.Context.getExternalFilesDirs(): returns an array of java.io.File representing the root directories of all the external storage directories that can be used by your application with the primary external storage as the first directory in the array. All those directories works the same as the primary storage returned by the getExternalFilesDir() method. If the device has a built-in storage as the primary external storage and a SD card as a secondary external storage, this is the only way to get the path to the SD card. This method was introduced in Android 4.4, before that it was impossible to get the path to the secondary storage.
android.content.Context.getExternalCacheDir(): returns a java.io.File object representing the cache of the application on the primary external storage. This cache is not visible to the user and is deleted when the application is uninstalled. There is no mechanism in the Android SDK to delete files in the cache directory, so you need to manage your cache to keep it to a reasonable maximum size. Starting with Android 4.4, the application does not need permission to access its own cache, but with older versions your application needs the READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE permission.
I need to download files on my mobile device and make them accessible for other apps (using Android and iOS).
I managed to download a file to the SD card (cordova.file.externalDataDirectory), but this only exists on Android and even then I cannot rely on every device having an SD card.
When I download to the device storage (cordova.file.dataDirectory), the file is private to my app and therefore not accessible for other apps. The file can be opened in the InAppBrowser, but I would prefer to use the respective default app.
Is there a way to get a path to a directory publicly available on all devices?
The paths returned by the solution suggested in https://stackoverflow.com/a/21375812/3432305 are both private on Android...
EDIT:
I think I should describe my use case so it's clearer what I'm trying to achieve: I want to open files from my in app chat using the respective default app (pdf viewer, image viewer etc.). Because the Cordova File Opener plugin only accepts files from the local file system, I need to save them first. But they don't necessarily need to be accessible from outside my app afterwards...
On Android, external storage directories always exist; if the device doesn't have a physical SD card, Android will emulate it. see getExternalStorageDirectory :
Note: don't be confused by the word "external" here. This directory
can better be thought as media/shared storage. It is a filesystem that
can hold a relatively large amount of data and that is shared across
all applications (does not enforce permissions). Traditionally this is
an SD card, but it may also be implemented as built-in storage in a
device that is distinct from the protected internal storage and can be
mounted as a filesystem on a computer.
Therefore cordova.file.externalDataDirectory will always resolve. However, for sharing data between apps, you probably want to use cordova.file.externalRootDirectory - External storage (SD card) root. See cordova-plugin-file.
This way you can store files in a place that's easier to access from another app e.g. /sdcard/my_shared_data/
On iOS, it's more difficult to share files because apps are intentionally isolated from each other due to security policy, as Apple's Inter-App Communication Guide says:
Apps communicate only indirectly with other apps on a device
You best bet on iOS is to share the data by synching it via iCloud. See the section Configuring a Common Ubiquity Container for Multiple Apps in iCloud Design Guide, which says:
... perhaps you provide two apps that interoperate and need
access to each other’s files. In both of these examples, you obtain
the needed access by specifying a common ubiquity container and then
requesting access to it from each app.
When using Eclipse file explorer to navigate my android directories, I saw mnt/sdcard and mnt/sdcard2, see below image:
When callingEnvironment.getExternalStorageDirectory() it returns mnt/sdcard, so I think the mnt/sdcad is the external storage , and mnt/sdcard2 is my actual SD card, is that true? And how can I use code to access files under mnt/sdcard2 ?
P.S.
It seems that I can access the external sd card directly:
File extStorageDir = Environment.getExternalStorageDirectory();
String parent = extStorageDir.getParent();
File extSdCardDir = new File(parent+"/sdcard2");
File file = new File(extSdCardDir, "DemoFile.jpg");
But I wonder the extra sd card will change name in other cases.
You are correct, getExternalStorage will return your built-in external storage. Unfortunately, as of Jelly Bean applications are no longer able to utilize the SD card if the device also has built-in storage as well as an SD card. You can try working around it through shell commands or hardcoding paths, but without root there is no reliable way to access it anymore.
This was just recently added the Android CTS, which all OEMs must comply with in order to use the Play store.
Compatibility Program Overview | Android Developers
Section 9.5 (pg. 34) of Android 4.3 Compatibility Definition
Device implementations that include multiple external storage paths
MUST NOT allow Android applications to write to the secondary external
storage.
Storage Options | Android Developers
It's possible that a device using a partition of the internal storage
for the external storage may also offer an SD card slot. In this case,
the SD card is not part of the external storage and your app cannot
access it (the extra storage is intended only for user-provided media
that the system scans).
Android 4.2 APIs | Android Developers
Saving data in a multi-user environment
Whenever your app saves user preferences, creates a database, or
writes a file to the user’s internal or external storage space, that
data is accessible only while running as that user.
To be certain that your app behaves properly in a multi-user
environment, do not refer to your internal app directory or external
storage location using hard-coded paths and instead always use the
appropriate APIs:
For access to internal storage, use getFilesDir(), getCacheDir(), or openFileOutput().
For access to external storage, use getExternalFilesDir() or getExternalStoragePublicDirectory().
No matter which of these APIs you use to save data for a given user,
the data will not be accessible while running as a different user.
From your app’s point of view, each user is running on a completely
separate device.
I actually want to access the files on android system. I manage to read the files and directories in the external storage of android.
My questions are :
How can I access the directories and files of the internal storage(Specially videos, pics all of the applications which runs in android).
Where are stored the data of each application which installed in internal storage?
Is it possible to transfer data which exists in the internal storage (from internal memory to external storage?
I read that external storage is world-readable? what does that mean?(if a create an application then this application can access all the files that installed in external storage?)
By default, an app cannot access data stored by another app. Permissions are applied to internal storage that make data written by an application not accessible outside of that application (your app cannot read anything written by another app). This can be changed, where an application can specify different permissions for ITS OWN data; basically, an app can allow others to read its data. However, if an app does not specifically set its permissions to allow this, other apps cannot access its data. This is a fundamental principle of the Android security/isolation model and is done at the Linux/kernel level, as each app runs under its own Linux UID and permissions are set within the filesystem only allowing that UID access to the app's directory structure (group and world permissions are set to 0 by default).
This all goes out the window if you have access to root on the device (rooted phone and your app runs with root permissions), but we should consider that out-of-scope for your question.
External (SD card) storage is different in that it is considered free-for-all and permissions are not applied there (this is originally due to the filesystem typically used in SD not supporting permissions). Any app can usually read anything written to the SD card by any other app, unless the original app does something to protect it (encrypt, etc).
This is all explained in great detail in Application Security for the Android Platform, just published by O'Reilly.
You have to write FileProvider to access the app specific files with another app.