I am really tired with debugging this problem. I have an application that writes to a storage. The application worked fine, until I got an update (I don't know which update caused it, because I had a break from Android development) - my Android version is 6.0.1 now.
Anyways I am trying to write to my external storage. I got prepared reading the new permission system for Android, got the permissions, and I am doing a check in my code:
checkSelfPermission("android.permission.READ_EXTERNAL_STORAGE")
checkSelfPermission("android.permission.WRITE_EXTERNAL_STORAGE")
By debugging I've checked (I also get a code that checks and asks if permission is necessary, but I wanted to be sure that it really is ;) ) I've observed that they return value 0, which is equal to PackageManager.PERMISSION_GRANTED
So, the permissions are granted for sure. I am trying to create an App directory in the storage to write pdf files, store sqlite DB and stuff like that.
Here is how I create the directory:
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// We can read and write the media
File dir = Environment.getExternalStorageDirectory();
String sNewDir = Constants.ApplicationDataDir;
if (sSubDir == null) {
sSubDir = "";
} else {
sSubDir = sSubDir.trim();
}
if (sSubDir.length() > 0) {
sNewDir += File.separator + sSubDir;
}
File dirName = new File(dir, sNewDir);
if (!dirName.exists()) {
if (!dirName.mkdirs()) {
LogDump.e("getAppDirectory err", " (sSubDir=<" + sSubDir + "> cannot create dir: " + dirName.getAbsolutePath());
}
}
return dirName;
} else {
// Something else is wrong
LogDump.e("getAppDirectory err", " (sSubDir=<" + sSubDir + "> not mounted state: " + state);
return null;
}
The value of dirName is /storage/emulated/0/MY_APP_CONST_VALUE/sSubDir (the sSubdir is a parameter for subdirectory ie. "Preferences")
Soo, the code (checking with debugger) the execution checks that the directory does not exist, and the mkdirs() returns false result. THAT actually means that the directory does not exist, and could not be created.
During my application operation my logs get filled with:
W/FileUtils: Failed to chmod(/storage/emulated/0/MY_APP_CONST_VALUE/DB/pcdrdata.sqlite3): android.system.ErrnoException: chmod failed: EPERM (Operation not permitted)
from the SQLLite code
And FileNotFound exception for any other file operations...
This seems quite logical since It cannot create the directories, right?
Now for the most confusing part:
I am using total commander, and I can see that the directories are actually being created, the sqlite database is being copied, and all the files that do FileNotFoundException are there. I made a test, deleted them, rerun the App, and they are copied to the location once again. However any read operation on the files is not possible.
So it somehow creates the directories, allows to write (even thouh mkdirs() says otherwise) to them, but does not allow to read.
I am really confused, and tired of trying. I also tried the same without the SD card - identical results though.....
My application creates PDF files - they are created successful, but even the Total Commander says that it cannot open them....
My phone is Xperia Z3
As of KitKat you can't read from the root of the external storage anymore. You can only read in special public directories or in your own private directory on the sdcard. If you're seeing them created but not able to alter them, it seems like Android decided you could write there but not read there
Related
I use this code to get folder of removable SD-card:
var baseDir = Environment.getExternalStorageDirectory().absolutePath
val dirs = getExternalFilesDirs(null)
for (file in dirs){
if(Environment.isExternalStorageRemovable(file)) {
baseDir = file.absolutePath
val idx = baseDir.indexOf("/Android/")
if(idx > 0) {
baseDir = baseDir.substring(0,idx)
break
}
}
}
And it work well enough, at least on test devices. After this code baseDir contain something like /storage/AB96-CD85. But when I try to make dir, I always get false result:
baseDir = baseDir + java.io.File.separator + name
var result: File? = null
if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED) {
Log.e(TAG, "SD-card not mounted.")
return result
}
val dir = File(baseDir)
if (!dir.exists()) {
if (!dir.mkdir()) {
Log.e(
TAG,
"Can't create directory $name"
)
return result
}
}
minSdkVersion is 21. Before this code execution I've already requested WRITE_EXTERNAL_STORAGE permission and got positive response from user. I wrote permission block in Manifest in correct place (out of <application></application> block). The code above create folder without any problems on one device and cannot do the same on other (in the same time ES File manager work well on sdcard). What was tried:
App re-installation
Phone restart
Even unplag USB cable
Storage Access Framework(SAF)
What I should do to make mkdir() function work on the second device?
If I should add some other permission or move user to some system settings screen, how can I do that?
Why code above work on one device and doesn't work on other?
I think it's about different Android versions.
To modify External SD card's filesystem, you need to get "special permision".
You can Google some libraries, that do it automatically. Just use one of these and you will gain that "special permission". It's weird but WRITE_EXTERNAL_STORAGE not providing you access to SD Card, only to eMMC memory.
I am trying to use Android's internal helpers to get a path from the system for my file first and then put my files where the system wants. Because tomorrow they might change their minds.
I made a simple program to explore this subject. Here is my code;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String path = letsMakeAfile(this, "myFile.txt");
}
private static String letsMakeAfile(Context context, String nameOfFile) {
String strOfFinalPath ="";
//ask the system what path to use...
String strOfContextPath = context.getFilesDir() + "/";
//line above doesnt work without ' + "/" ' or something on the end
//line above records this path: /data/user/0/com.example.filesexperimenting/files/
//this appears to be an invalid path unless "user" is a hidden directory
Log.d("IDIOT", "strOfContextPath: "+ strOfContextPath);
try
{
File file = new File(strOfContextPath, nameOfFile);
if (file.exists() == false) {
file.mkdirs();
//after this line "makes dirs" is file automatically still made and dropped in?
letsMakeAfile(context, nameOfFile);
//I assume not so Ive made a recursive call
}
else
;
//escape recursion....
strOfFinalPath = file.getAbsolutePath();
//Here I record the path where I hope the file is located
Log.d("IDIOT", "strOfFinalPath: "+ strOfFinalPath);
}
catch (Exception e) {
e.printStackTrace();
Log.d("IDIOT", "CATCH ERROR: "+ e.getLocalizedMessage());
}
//runs without a catch
return strOfFinalPath;
}
}
Logcat:
2019-04-09 09:59:22.901 16819-16819/? D/IDIOT: strOfContextPath: /data/user/0/com.example.filesexperimenting/files/
2019-04-09 09:59:22.901 16819-16819/? D/IDIOT: strOfFinalPath: /data/user/0/com.example.filesexperimenting/files
Ultimately I am getting a path of /data/user/0/com.example.filesexperimenting/files/ from context.getFilesDir() which appears to be an invalid path unless "user" is a hidden directory (then why can I see root?). In Device File Explorer under data the only other directories are app, data and local
What am I missing? I'll assume its something with file.makedirs()
Full disclosure, I am a student and there is not a lot out there on this so your replies, while obvious to you at your experience level, should help others. I have some experience with Java and more with C++ but Android is new to me. Thanks in advance!
So, in talking outside of StackExchange it appears that using java.io like I am trying to in the example can cause some problems because of the preset file directories that may be locked or restricted that Java io might not know about.
Android has it's own method openFileOutput(String name, int mode) that has the ability to create the app resource file and directory it belongs in.
Description copied from class: android.content.Context
Actions:
~Open a private file associated with this Context's application package for writing.
~Creates the file if it doesn't already exist.
~No additional permissions are required for the calling app to read or write the returned file.
Params:
~name – The name of the file to open; can not contain path separators.
~mode – Operating mode.
Returns: The resulting FileOutputStream.
Throws: java.io.FileNotFoundException
If you want to be able to navigate to the location of your saved files through the file explorer (either in Android Studio or the Files app on the phone) you should use Context.getExternalFilesDir().
Context.getFilesDir() returns a directory not accessible by anyone BUT the creating application. So if you would like to see what is in this file you would need to open it with the same application that wrote it. IE: Print the contents to the screen after you save it in your app.
Context.getExternalFilesDir() returns a directory completely accessible by anyone and any application. So files created and saved in this external directory can be seen by Android Studio's file explorer as the OP has screenshot or by any application installed on the phone.
What is nice about both of these methods is that as long as you are only accessing files you have created you never need to ask the user for storage permissions Read or Write. If you would like to write to someone else's external files dir then you do.
Source
Check if sdcard is mounted or not.
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)){
///mounted
}
Get the path of sd card
File dir= new File(android.os.Environment.getExternalStorageDirectory());
walkdir(dir);
ArrayList<String> filepath= new ArrayList<String>();
//list for storing all file paths
public void walkdir(File dir) {
File listFile[] = dir.listFiles();
if (listFile != null) {
for (int i = 0; i < listFile.length; i++) {
if (listFile[i].isDirectory()) {
// if its a directory need to get the files under that directory
walkdir(listFile[i]);
} else {
// add path of files to your arraylist for later use
//Do what ever u want
filepath.add( listFile[i].getAbsolutePath());
}
}
}
}
Try using this:
context.getFilesDir().getParentFile().getPath()
After going through the headache of figuring out how to make a large SD card (~32gb) using mksdcard and pushing information to it (slowly) using ADB, I can go on to my AVD manager, start up my Nexus 4 (with all default settings), and go to
Settings > Device > Storage & USB > Portable Storage > SDCARD
I see all of the files I expect to see there (i.e. all the default stuff plus everything I pushed).
However, in my app, it tells me there's nothing there. I have a menu item that calls this simple function, but its results are baffling.
public void dumpSDCardContents(MenuItem m) {
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
recursFiles(Environment.getExternalStorageDirectory());
}
}
private void recursFiles(File dir){
System.out.println("Now scanning " + dir.getAbsolutePath());
File[] listFiles = dir.listFiles();
if(listFiles != null){
for(File file : listFiles){
System.out.println(file.getAbsolutePath());
if(file.isDirectory()){
recursFiles(file);
}
}
} else {
System.out.println("That directory has nothing in it.");
}
}
All I see in logcat is
Now scanning /storage/19E8-1D10
That directory has nothing in it.
Which is bollocks. If I go to the File Explorer tab in DDMS, I can see the web of linking from /mnt/sdcard/ to /sdcard/ to /storage/self/primary/ to /mnt/user/0/primary/ to (finally) /storage/19E8-1D10/ and then looking in that directory, plain as day, is all the data I'm looking for.
I was sure to add
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
to my AndroidManifest.xml, and besides, the javadoc comment on Environment.MEDIA_MOUNTED (which I already successfully tested against) says "Storage state if the media is present and mounted at its mount point with read/write access."
What am I missing here???
-
UPDATE:
So I changed the recursFiles function to dump file permissions
private void recursFiles(File dir){
System.out.println("Now scanning " + dir.getAbsolutePath());
System.out.println("Can r dir? " + dir.canRead());
System.out.println("Can w dir? " + dir.canWrite());
System.out.println("Can x dir? " + dir.canExecute());
File[] listFiles = dir.listFiles();
if(listFiles != null){
for(File file : listFiles){
System.out.println(file.getAbsolutePath());
if(file.isDirectory()){
recursFiles(file);
}
}
} else {
System.out.println("That directory has nothing in it.");
}
}
and (not terribly surprisingly given the original output) it tells me
Now scanning /storage/17F2-2913
Can r dir? false
Can w dir? false
Can x dir? true
That directory has nothing in it.
So. Why would the Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) check come back true (implying I have both read and write) when I in fact have neither read nor write permissions??
And how do I fix this?
I noticed when I went into the emulator using adb shell, that permissions files (i.e. /system/etc/permissions/platform.xml and /etc/permissions/platform.xml) flat out don't even have a permission tag for WRITE_EXTERNAL_STORAGE.
How do I rectify all this??
I have a Samsung Nexus S device with android 4.0 loaded on it. I am trying to create a file in an existing folder on sdcard and get a "permission denied". In the following code, exists() returns true but canWrite() returns false. Why?
File exst = Environment.getExternalStorageDirectory();
String exstPath = exst.getPath();
File d = new File(exstPath+"/TestDir/");
if (!d.exists())
{
int b = 1;
}
if (!d.canWrite())
{
int a = 1;
}
By the way, I've added <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> to the manifest but that did not help.
Is your phone plugged into your computer? If so, the computer will take control of the SD card and not allow it to be written to. Try changing the connection mode to 'Charge Only' if this is the case.
Append getAbsolutePath() to your first line, then it should work:
File exst = Environment.getExternalStorageDirectory().getAbsolutePath();
Update:
Reviewing my own code and other SO answers, I believe you do not use canWrite to check if a path is writable on a SD card. Instead you use Environment.MEDIA_MOUNTED:
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
Log.d("Test", "sdcard mounted and writable");
}
else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
Log.d("Test", "sdcard mounted readonly");
}
else {
Log.d("Test", "sdcard state: " + state);
}
Although all the answers give partial suggestions, the problem is very likely Samsungs non-standard implementation of the API
String myPath = Environment.getExternalStorageDirectory().getAbsolutePath ;
// or .getName() or .getPath() <-- these don't return everything you need.
// same is true for the file version you're trying.
// you have to append
"/external_sd/"
to the path before the file name.
Here's Samsungs explanation for their "breaking" of the API
http://developer.samsung.com/forum/board/thread/view.do?boardName=GeneralB&messageId=162934&messageNumber=1381&startId=zzzzz~&searchType=TITLE&searchText=sdcard
it's also mentioned in several SO posts, but I don't have the links handy...
H
[Edit Mid May, 2013] Here's the pathology of this problem: you can get the path() by the various normal methods mentioned. Then, just write a simple file and watch it show up in the DDMS file explorer. Try hardcoding that exact path and file name OR use the /external_sd/ thing - in both cases, you will not be able to read your own file back in ! (Or you might, but it will contain garbage.) We've verified this on three different S3 phones. Will test more on Samsungs "real" phones via their RemoteTestingLab site and report back.
I notice that my app's data on external storage (i.e. SD card) gets deleted during an "adb install -r". While this is normal for uninstall (and then afterwards install optionally to notice it), I do not understand why this is the case for reinstall (and thus for Market updates as well). I could have sworn this was not always the case.
Referring to http://developer.android.com/guide/topics/data/data-storage.html#filesExternal I am specifically using "Accessing files on external storage" on Android 2.2, but not "Saving files that should be shared" or "Saving cache files". So I am writing and reading data in "/sdcard/Android/data//files/somefolder/data". My preferences do stick.
#Commonsware: The problem is not so much with getExternalFilesDir() IMHO as I see my data is written where I expect it. It just does not stick. I am using a.o.:
public static File getDefaultDirectory(Context context, String packageName) {
File dir;
if(mActivity_getExternalFilesDir!=null){//API >=8
dir = invokeGetExternalFilesDir(context, "fortune");
}else if(mEnvironment_getExternalStorageDirectory!=null){//API <=7
dir = invokeGetExternalStorageDirectory();
dir = new File(dir.getPath() + "/Android/data/" + packageName + "/files/");
}else{
//should never occur
return null;
}
return dir;
}
IIRC, there is a bug in Android 2.2 that causes these symptoms. I advise against the use of getExternalFilesDir() until Gingerbread.