I have an application where I test all storages whether they are accessible. As you probably know, Google starts to limit access to external storages. So Kitkat devices can have read-only storages that have writable access only to application specific directory (/Android/data/...).
I test paths of all storages whether they are writable by testing:
if(new File("/storage_root_path/").canWrite() && new File("/storage_root_path/AppDir/").canWrite())
{
//storage is writable
}
else
{
//storage is readonly
}
/AppDir/ is directory of my app on root of storage. These tests work well for most devices. But I have one user with Samsung Galaxy S4 SGH-M919 (Kitkat) and both tests return true on his external sd card. But the storage is not writable.
Is there any other way how to determine that storage is read-only on Kitkat ? I can try to create folder but I would like to have some better and faster solution.
Thank you for any advice!
Confirm. One should really check file for writability, File.canWrite() doesn't help. I'm using this kind of function:
private boolean isFileWritable(File file) {
try {
RandomAccessFile raf = new RandomAccessFile(file, "rw");
FileChannel fc=raf.getChannel();
FileLock fl=fc.lock();
fl.release();
fc.close();
raf.close();
return true;
}
catch(Exception ex) {
return false;
}
}
Related
I have searched everywhere on stackoverflow, there are plenty of similar questions, but the answer always ends up with something that helps write on the internal storage.
I am using the File Browser snippet available here: https://github.com/vaal12/AndroidFileBrowser
After that, I get the path to the directory where I want to create my Folder.
try {
print(Environment.getExternalStorageDirectory().getAbsolutePath()); //return something like "/storage/sdcard0/"
String realPath=newDir +"/"+ getString(R.string.folder_name); //returns something like "/storage/extSdCard/..."
//realPath=realPath.replaceAll("storage", "mnt");
print(realPath);
File f = new File(realPath);
if (!f.exists()) {
f.mkdirs();
}
} catch(Exception e) {
print(e.toString());
}
This works fine when the chosen directory is on the internal sd card, but nothing happens (not even an IOException) when it's on the removable external sd card
There is good description and example about saving onto external storage on the android developer webpage. I think this would help you: http://developer.android.com/guide/topics/data/data-storage.html#filesExternal
I want to save a text file to the SD card I inserted into my HTC One M8 running lollipop. However when I run this code it saves to internal storage instead.
String FILENAME = "mysavefile.txt";
File file = new File(Environment.getExternalStorageDirectory(), FILENAME);
if (isExternalStorageWritable()) {
errorSD.setVisibility(View.INVISIBLE);
try {
FileOutputStream fos = new FileOutputStream(file, false);
fos.write(allInformation.getBytes(), 0, 81);
fos.close();
successfulSubmissionToast();
} catch (FileNotFoundException e) {
e.printStackTrace();
errorSD.setVisibility(View.VISIBLE);
} catch (IOException e) {
e.printStackTrace();
}
It should be saving to
/storage/ext_sd
but instead it is saving to
/storage/emulated/0
Then I tried manually entering in the location of my SD card to see if that would work but it ended up throwing the FileNotFoundException
File file = new File("/storage/ext_sd", FILENAME);
Edit:
I believe the issue is that there are multiple external storages. One being permanent and one temporary. The question is how do you access the second one.
First of all, we need to understand about what is 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
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.
getExternalStorageDirectory ()
It will return path of the primary external storage directory
To access removable SD (Secondary external storage) there are below APIs:
getExternalFilesDirs(), getExternalCacheDirs(), and getExternalMediaDirs()
Check there documentation for further information
If the Android device following the guide here when deal with multiple external storage, we can use the following code to detect where to write the data for Android kitkat or higher version.
final String APP_EXTERNAL_CACHE = "Android" + File.separator + "data"
+ File.separator + getPackageName() + File.separator + "cache";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
for (File file : getExternalCacheDirs()) {
if (file != null) {
String mountPoint = file.getAbsolutePath().replace(APP_EXTERNAL_CACHE, "");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (Environment.isExternalStorageRemovable(file)) {
Log.d(TAG, "removable external " + file.getAbsolutePath());
}
if (Environment.isExternalStorageEmulated(file)) {
Log.d(TAG, "emulated internal " + file.getAbsolutePath());
}
} else {
if (mountPoint.contains("emulated")) {
Log.d(TAG, "emulated internal " + mountPoint);
} else {
Log.d(TAG, "removable external " + mountPoint);
}
}
}
}
}
And declare the relating permission in the manifest.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
I want to save a text file to the SD card I inserted into my HTC One M8 running lollipop
You are welcome to try getExternalFilesDirs(), getExternalCacheDirs(), and getExternalMediaDirs() on Context. Note the plural form of the method name. For these methods, if they return more than one entry, the second and subsequent ones should be on removable media, pointing to directories that you can read and write, without any particular permission (e.g., WRITE_EXTERNAL_STORAGE).
Outside of those locations, for devices that shipped with Android 4.4+, you have no access to removable storage.
I think it's because HTC one modified the sdcard mount point, you should try adb shell ls -l commands to find out which path the sdcard mounted.
For example in nexus 4:
lrwxrwxrwx root root 2015-03-24 18:26 sdcard -> /storage/emulated/legacy
drwxr-x--x root sdcard_r 2015-03-24 18:26 storage
and you should make sure it is not a link: ls -l /storage/emulated/legacy
lrwxrwxrwx root root 2015-03-24 18:26 legacy -> /mnt/shell/emulated/0
The path /mnt/shell/emulated/0 is actually sdcard path.
Try read How can I get external SD card path for Android 4.0+? too, this may help you a lot.
UPDATE
I tried this code:
String test = "/storage/emulated/legacy"; // work
//String test = "/mnt/shell/emulated/legacy"; // not work
File sdDir = new File(test);
if (sdDir.exists()) {
File file = new File(test, "test.sdcard");
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
So you'd better try /storage/emulated/* directories to find out which is exactly your sdcard.
I'm trying to write a file to my phone.
I used Environment.getDataDirectory() to know the internal storage's path and Environment.getExternamStorageDirectory() to know the external storage's path.
But when I use Environment.getExternalStorageDirectory() as path, the file is created in internal storage. And when I use Environment.GetDataStorage() as the path, the file is not created. (I am not sure, but I can't find it in the explorer app, at least.)
I think my phone's internal storage is perceived as external storage.(In my case, it has 32 GB amount of storage)
I want to know removable storage(e.g. micro SD card) path. What should I do?
From the official documentation for getExternalStorageDirectory()
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.
So, it can be different from built-in storage in a device.
For your case, you could use getExternalStoragePublicDirectory(java.lang.String)
This is where the user will typically place and manage their own
files
The path here should be one of DIRECTORY_MUSIC, DIRECTORY_PODCASTS,
DIRECTORY_RINGTONES, DIRECTORY_ALARMS, DIRECTORY_NOTIFICATIONS,
DIRECTORY_PICTURES, DIRECTORY_MOVIES, DIRECTORY_DOWNLOADS, or
DIRECTORY_DCIM. May not be null.
Or if you want your data to be deleted whenever the user uninstalls your app, you could use getExternalFilesDir().
As these files are internal to the applications, and not typically visible to the user as media.
Also there are some differences between getFilesDir() and getExternalFilesDir()
External files are not always available: they will disappear if the user mounts the external storage on a computer or removes it. See the APIs on environment for information in the storage state.
There is no security enforced with these files. For example, any application holding WRITE_EXTERNAL_STORAGE can write to these files.
Try this...
static String storagestate = Environment.getExternalStorageState();
private static FileOutputStream outStream;
private static File imageFilepath;
public static String saveImage(Bitmap bitmap) {
File folder = null;
// Check for SD card
if (storagestate.equals(Environment.MEDIA_MOUNTED)) {
folder = new File(Environment.getExternalStorageDirectory(),
"*YourStorageNameInDevice");
if (!folder.exists()) {
folder.mkdir();
}
outStream = null;
String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")
.format(new Date());
// Getting filepath
imageFilepath = new File(folder.getPath() + File.separator
+ timestamp + ".PNG");
try {
outStream = new FileOutputStream(imageFilepath);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);
outStream.flush();
outStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return imageFilepath.getAbsolutePath();
}
}
In my app, im trying to check whether the user has connected their phone to the pc as a drive, so I can warn them to disconnect it because I need access to the storage.
It works fine on all 4-5 devices that I've tested on, except this HTC Desire X phone, Android 4.0.4 . It has no SD card, but there is about 2 gb of some storage available for writing.
Here is the code that I use
private void checkStorage() {
// Get the external storage's state
String state = Environment.getExternalStorageState();
Log.d("STATE", state);
if (state.equals(Environment.MEDIA_MOUNTED)) {
// Storage is available and writeable
externalStorageAvailable = externalStorageWriteable = true;
} else if (state.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
// Storage is only readable
externalStorageAvailable = true;
externalStorageWriteable = false;
} else {
// Storage is neither readable nor writeable
externalStorageAvailable = externalStorageWriteable = false;
}
}
So when I run it, the logcat debug tag returns:
removed
According to documentation:
http://developer.android.com/reference/android/os/Environment.html#MEDIA_REMOVED
"Storage state if the media is not present."
How can I modify this code so it detects the presence of a memory that is available for use?
Now, there is another thing. On this phone, when I try downloading an image from the browser, Im not allowed to, and I get the following message:
No SD card
An SD card is required to download <filename>
OK
Is there something wrong with the phone or is this normal? How do I make my app work on this phone?
EDIT: Furthermore, if I am able to get past this, how do I write files to the storage? Here's my code that does that and works on other devices:
File directory = null;
File file = null;
try {
directory = new File(Environment.getExternalStorageDirectory().getAbsolutePath(),
Utils.getWritableDiectory(Locale.ENGLISH));
if (!directory.exists()) {
directory.mkdirs();
}
file = new File(directory, "data.json");
I have phone (B15 CAT) with a sd card slot. When i insert a sdcard in this phone and asking for the external storage directory with :
Environment.getExternalStorageDirectory()
it always return an space on sdcard0 which is the internal memory. This memory is too small for my need.
By listing /mnt i found a mount point named /sdcard2 which is the "real" scard.
Unfortunately sdcard2 doesn't seems to be a standard and some other brand will use some other name...
Knowing that getExternalStorageDirectory() seems working as expected on phone with no sdcard slot , like nexus 4, how should i handle external storage to be sure to write on the sdcard (big space available) and not on internal memory ?
I have tried something like this :
File mnt = new File("/mnt");
File[] liste = mnt.listFiles();
boolean hassd2 = false;
for(File mount : liste) {
if(folder.getName().equals("sdcard2") {
hassd2 = true;
break;
}
}
String path = "";
if(hassd2) {
path = "/sdcard2/my/folder/"
} else {
File p = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/my/folder/");
path = p.toString();
}
It's working but only with this specific phone and others one with no sdcard slot ...
I also had the problem with the build in functions of Android in case of multiple 'external' storages mounted. I parsed the mounted directories directly from the f_stab file.
This link should give you the code you needed.
After having the mount points you could try to calculate the available space in order to decide if it is enough for your operation.