I read alot about KITKAT and the prevention of writing to the secondary external sdcard.
It seems to be a new policy from google. I also read about storing the app's data under /externalsdcard/Android/data/.
Now my question, because I can't reproduce or test:
If the app is not allowed to write data directly under
< secundary external sdcard >, Will it then be possible to store the app's data under < secundary external sdcard >/Android/data/< app package name > ?
If possible. Could someone share a codesample ?
Never assume the removable secondary storage is larger than primar internal. On most modern devices internal storage can be up to 64 GB, but SD Card can be as small as 1 MB large (if user inserted small old card).
There is no way to distinguish which one is "external" using current APIs.
So the right way is using Context.getExternalFilesDirs() which returns all external storages to which you can store application-specific files without any permissions.
Use StatFs to find the largest storage available, and write data into it.
final File[] dirs = context.getExternalFilesDirs(null); //null means default type
//find a dir that has most of the space and save using StatFs
As the documentation says,
Returns absolute paths to application-specific directories on all external storage devices where the application can place persistent files it owns. These files are internal to the application, and not typically visible to the user as media.
This is like getFilesDir() in that these files will be deleted when the application is uninstalled, however there are some important differences:
External files are not always available: they will disappear if the user mounts the external storage on a computer or removes it.
There is no security enforced with these files.
External storage devices returned here are considered a permanent part of the device, including both emulated external storage and physical media slots, such as SD cards in a battery compartment. The returned paths do not include transient devices, such as USB flash drives.
An application may store data on any or all of the returned devices. For example, an app may choose to store large files on the device with the most available space, as measured by StatFs.
No permissions are required to read or write to the returned paths; they are always accessible to the calling app. Write access outside of these paths on secondary external storage devices is not available.
The first path returned is the same as getExternalFilesDir(String). Returned paths may be null if a storage device is unavailable.
Related
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.
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.
So I'm testing an app on a galaxy s3 on 4.1 and an older phone that runs 2.3. Neither phone has an sdcard in it but when I use Environment.getExternalStorageDirectory().getAbsolutePath() on my galaxy s3 it directs me to a folder called sdcard0 within a folder called storage. When I run it on the older phone it gives me the path mnt/sdcard/ and the sdcard folder is read only.
I am trying to create a folder in these directories. I can do it on my s3 but not on my older phone.
Is there something similar to the storage folder I'm missing on the older phone or can I write to the sdcard folder when there is no sd card present?
EDIT: I have the external write permissions in my manifest
On some phones, as the documentation suggests, the word external is not to be taken all too serious:
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.
You can check if the storage is really external or not, using the Environment.isExternalStorageRemovable()-method. If it's not removable, you should always be able to write to it (given that you have the permissions declared).
If it is however removable, you'll need to check it's current state with Environment.getExternalStorageState(). Here's a quick example of how to use it:
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// We can read and write!
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
// Mounted read only...
} else {
// Something is wrong...
}
So in these cases, you can write to the external storage.
Important: As the documentation specifies at multiple points, the above methods work for the primary external storage. Some devices (like the Motorola Xoom) have both an internal "external" storage, and an SD-Card.
There seems to be no "generalized" way to access the "secondary external storage" on such devices, although there is a standard now, introduced with Android 4.2
Devices may contain multiple instances of external storage, but
currently only the primary external storage is exposed to developers through API.
[...]
Starting in Android 4.2, devices can support multiple users, and
external storage must meet the following constraints:
[...]
Secondary external storage must not be writable by apps.
It seems to depend on the vendor which storage (internal or real external) is considered the primary one. Not much you can do here (without things getting messy...)
If there is no external storage present, you can use your applications Internal Storage, or the caching directory, depending on what kind of data you're storing.
In my application , downloading lot of files from server. I want to cache them in sdcard.
for that am using fallowing api..
context.getExternalCacheDir();
But problem is that, not able to save them in internal sdcard(i.e; non removable external storage).They are saving in to "/mnt/sdcard/external_sd/Android/data/".
Please gimme a way to save may files in to non removable android cache.
Regards,
Srinivas
From intellisense for Environment.getExternalStorageDirectory:
"Gets the Android external storage directory. This directory may not currently be accessible if it has been mounted by the user on their computer, has been removed from the device, or some other problem has happened. You can determine its current state with getExternalStorageState().
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.
In devices with multiple "external" storage directories (such as both secure app storage and mountable shared storage), this directory represents the "primary" external storage that the user will interact with.
Applications should not directly use this top-level directory, in order to avoid polluting the user's root namespace. Any files that are private to the application should be placed in a directory returned by Context.getExternalFilesDir, which the system will take care of deleting if the application is uninstalled. Other shared files should be placed in one of the directories returned by getExternalStoragePublicDirectory(String)."
From the official documentation:
Using the External Storage
Every Android-compatible device supports a shared "external storage"
that you can use to save files. This can be a removable storage
media (such as an SD card) or an internal (non-removable)
storage ...
I'm really confused about this subject. From what I understand, using external storage doesn't necessarily mean to use a removable card, am I right? However, when talking about external storage, it's always referred as "sd card".
I'm developing an app that downloads .mp3 files from the internet. I want to save those files in the phone memory (don't want to use any removable device) but for what I have learned, those files have to be saved in external memory.However, I would like to offer the possibility of importing a file from a removable device. Where and how should I save those files?
Thanks
Difference between Internal Storage, External Storage (aka primary external storage) and Secondary External Storage?
Internal Storage: is storage that is not accessible by the user, except via installed apps (or by rooting their device). Example: data/data/app_packageName
External Storage has two types:
Primary External Storage: In built shared storage which is "accessible by the user by plugging in a USB cable and mounting it as a drive on a host computer". Example: When we say Nexus 5 32 GB.
Secondary External Storage: Removable storage. Example: SD Card.
When building an app that uses the internal storage, the Android OS creates a unique folder, which will only be accessible from the app, so no other app, or even the user, can see what's in the folder.
The external storage is more like a public storage, so for now, it's the sdcard, but could become any other type of storage (remote hard drive, or anything else).
The internal storage should only be used for application data, (preferences files and settings, sound or image media for the app to work).
If you intent to download many mp3s, i'd reccomend saving them to external storage, as the external storage is often bigger. Besides, storing data on the internal storage may prevent the user to install other applications.
The Internal and External Storage terminology according to Google/official Android docs is quite different from what we think.
According to official Android docs:-
Internal Storage: By default, files saved to the internal storage are private to your application and other applications cannot access them. When the user uninstalls your application, these files are removed/deleted. Your app user also can't access them using file manager; even after enabling "show hidden files" option in file manager. To access files in Internal Storage, you have to root your Android phone. So, this is NOT what we think as internal memory of the phone - Nexus 5's 32 GB internal memory.
External Storage:
This can be a removable storage media (such as an SD card) or an
internal (non-removable) storage
That means, both storage types like Nexus 6P's 64 GB internal memory and removable microSD card which we insert in phone's card slot are considered as External Storage.
Removable Storage means just microSD card storage, not the internal memory.
To store your app files in SD card, you may use File[] getExternalFilesDirs (String type) method in Context class. Generally, second returned path would be the storage path for microSD card (if any).
Note: I have edited - made my answer more useful after #Tunaki's comment.
From the Developer docs
All Android devices have two file storage areas: "internal" and "external" storage. These names come from the early days of Android, when most devices offered built-in non-volatile memory (internal storage), plus a removable storage medium such as a micro SD card (external storage). Some devices divide the permanent storage space into "internal" and "external" partitions, so even without a removable storage medium, there are always two storage spaces and the API behavior is the same whether the external storage is removable or not.
I think in the operating system, it defines external storage as anything not related to the actual OS filestructure. If you recall, when you write to 'internal storage', Android will make a folder privately for your application. So basically, if this is a hidden folder of some kind, it would mean that external storage could qualify as anything not being automatically hidden or managed directly by the OS. So this would mean that it would be up to the phone manufacturer about the definition of internal storage, as they could have 1 main piece of internal flash memory with two partitions on it. One partition meant to hold the os and the other meant to let you store everything on the phone.
Basically what I'm saying is: That's more a hardware related thing, and that the concept of 'external storage' could extend even to extra internal storage (flash memory) that the manufacturer added in. You could even consider storage options defined by the user as external storage as well.
Here's an updated answer for the latest Android (currently Android 13).
Internal storage used to mean the phone's internal memory and external storage used to mean, among other things, any inserted SD cards. Nowadays, this is not really the case because phones don't have SD cards any more. Phones without SD card slots still have "external storage" from the point of view of an app (eg Environment.getExternalStorageDirectory() still returns a valid location), but it's emulated - meaning it's actually a slice of internal storage.
This means one of the big differences between external and internal storage - which was that external was slower but bigger and internal fast and small - is no longer true.
Since Android 11, external storage has been scoped. This means apps get a folder of their own which is readable to them and unreadable to any other app (though see below). They can access this folder without permissions. Again, this brings external storage in line with internal. (Note scoped storage actually appeared in Android 10 but has only been enforced since Android 11).
But there are remaining differences between internal and external.
One big difference is that apps can still get a permission to read/write across all of external storage (MANAGE_EXTERNAL_STORAGE). This is hard to come by - users need to manually enable it by going into Settings, and apps which implement it are severely limited on the Play Store.
But it does mean that data written to external storage is less secure than that written to internal. Other apps may be able to eavesdrop on external.
The above is the TL;DR but there's a bit more to it. Here's a good article: https://medium.com/#tdcolvin/demystifying-internal-vs-external-storage-in-modern-android-c9c31cb8eeec