Question

In my android application, I am trying to create the following folder on the sdcard:

/mnt/sdcard/OSGiComponents/admin/felix-cache/

Here's the code:

File cacheDir = 
    new File( Environment.getExternalStorageDirectory().getAbsolutePath() + 
              "/OSGiComponents/admin/felix-cache/" );
// Create the folder
cacheDir.mkdirs();
// Check if it exists
if ( ! cacheDir.exists() ) {
    Log.e ( "Debug" , "Cache directory cannot be created" );
}

I have the WRITE_STORAGE_PERMISSION under the manifest tag of the android manifest file. I am able to create other folders and files without problem on the sdcard. The app works fine on the following phones:

  1. Nexus S (rooted) running Gingerbread (2.3)
  2. Nexus S (unrooted) running Jelly Bean (4.1.2)
  3. HTC Desire (rooted) running Froyo (2.2)
  4. HTC Desire (unrooted) running Froyo (2.2)

However on Samsung Galaxy Nexus phone (unrooted) running Ice Cream Sandwich (4.0.4), the directory is created as a zero size file, which can be seen in Astro. The exists() call returns false.

Astro showing a zero byte felix-cache file

  1. As you can see from the folder name, I am using Apache Felix. Felix creates the cache directory automatically if it does not exist. On Galaxy Nexus, it always complained that it is unable to create the cache directory. Astro shows a 0 byte file instead of a folder. This is why I decided to try creating the cache folder myself before initializing Felix.
  2. So, I create the cache folder myself. The app works fine the first time, and I can see the folder fine in Astro. If I close the app, then delete the folder in Astro, and then re-launch the app, even my code mysteriously cannot create the cache directory, and Astro shows a 0 byte file.
  3. The 0 byte file cannot be deleted in Astro. However, when I reboot the phone, the folder is magically there and ok.
  4. I use FileInstall to watch the OSGiComponents/install folder. When I drop bundle jars into that folder, it is detected and installed ok on all phones except Galaxy Nexus (when the app works the first time). There are no logs/errors from FileInstall about not being able to watch the directory.
  5. I have tested this on 2 Galaxy Nexus phones, same problem.

I suspect it is a permissions problem, but I not sure what it is, and why a 0 byte file is created while exists() returns false. Nowhere else in the code am I creating this file.

Any suggestions on what could be the problem?

Thanks :)

UPDATE: I think I have identified the issue, please see the answer I posted.

Was it helpful?

Solution 2

I found a workaround which solves this problem. Whenever I am deleting a file/directory, instead of using delete() directly, I rename the file/folder, and then delete() it. This weird workaround seems to remove the problem.

I got this idea by seeing this question's answers - Open failed EBUSY device or Resource busy

However, I'm not sure why this works, or what caused the problem in the first place.

In case anyone else is using Felix on Galaxy Nexus and encounters the same problem, just change the Felix source code as shown below:

org.apache.felix.framework.util.SecureAction.java:

public boolean deleteFile(File target)
{
    if (System.getSecurityManager() != null)
    {
        try
        {
            Actions actions = (Actions) m_actions.get();
            actions.set(Actions.DELETE_FILE_ACTION, target);
            return ((Boolean) AccessController.doPrivileged(actions, m_acc))
                .booleanValue();
        }
        catch (PrivilegedActionException ex)
        {
            throw (RuntimeException) ex.getException();
        }
    }
    else
    {
        // Solution: Rename before deleting
        // https://stackoverflow.com/questions/11539657/open-failed-ebusy-device-or-resource-busy

        File to = new File(target.getAbsolutePath() + System.currentTimeMillis());
        boolean renameStatus = target.renameTo(to);
        boolean deleteStatus = to.delete();
        boolean returnStatus = ( renameStatus && deleteStatus );

        // Debug SecureAction
        //boolean returnStatus = target.delete();
        Log.e ( "SecureAction" , "Deleting " + target + " delete(): " + returnStatus );
        return returnStatus;
    }
}

OTHER TIPS

please use instead of

   File cacheDir = new File( Environment.getExternalStorageDirectory().getAbsolutePath() + 
          "/OSGiComponents/admin/felix-cache/" );
      cacheDir.mkdirs();

to

  File cacheDir = 
new File( Environment.getExternalStorageDirectory().getAbsolutePath() + 
          "/OSGiComponents/admin/felix-cache" );
     cacheDir.mkdir();

I suggest that you connect to the device using adb and use the command ls -l in the directory, to check what is operating system reporting about this 0 size file (permissions, etc.). This can eventually bring some light to the issue.

If you can't figure out a working solution, maybe you can make a workarround using the exec() to execute a mkdir directly.

You can use the cobe bellow to do it:

public static boolean execCmd(String command, ArrayList<String> results){
    Process process;
    try {
        process = Runtime.getRuntime().exec(new String [] {"sh", "-c", command});
    } catch (IOException e) {
        e.printStackTrace();
        return false;
    } 

    int result;
    try {
        result = process.waitFor();
    } catch (InterruptedException e1) {
        e1.printStackTrace();
        return false;
    }

    if(result != 0){ //error executing command
        Log.d("execCmd", "result code : " + result);
        String line; 
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); 
        try {
            while ((line = bufferedReader.readLine()) != null){
                if(results != null) results.add(line);
                Log.d("execCmd", "Error: " + line);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return false;
    }

    //Command execution is OK
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream())); 

    String line; 
    try {
        while ((line = bufferedReader.readLine()) != null){
            if(results != null) results.add(line);
            Log.d("execCmd", line);
        }
    } catch (IOException e) {
        e.printStackTrace();
        return false;
    }
    return true;
}

To use it:

boolean res= execCmd("mkdir "+ Environment.getExternalStorageDirectory().getAbsolutePath() + "/OSGiComponents", results);
if(!res) return error;   

res= execCmd("mkdir "+ Environment.getExternalStorageDirectory().getAbsolutePath() + "/OSGiComponents/admin", results);
if(!res) return error;   

res= execCmd("mkdir "+ Environment.getExternalStorageDirectory().getAbsolutePath() + "/OSGiComponents/admin/felix-cache", results);
if(!res) return error;   

Regards.

String root = Environment.getExternalStorageDirectory()
            .getAbsolutePath()+"/OSGiComponents/admin/felix-cache";
    File myDir = new File(root);

    String fname = "Image Name as you want";
    File file = new File(myDir, fname);
    if (file.exists())
        file.delete();
    try {
        FileOutputStream out = new FileOutputStream(file);
        bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
        out.flush();
        out.close();

    } catch (Exception e) {
        e.printStackTrace();
    }

Although not a direct answer to your question,the information mentioned below may help you:

In certain devices, the Environment.getExternalStorageDirectory().getAbsolutePath() does not necessary reflect the actual external sd card path. Sometimes it represents the internal storage.

for example in Galaxy Note 2:

Log.i(TAG,Environment.getExternalStorageDirectory().getAbsolutePath()); 

will print

12-19 17:35:11.366: E/sample.Examples(10420): /storage/sdcard0

meanwhile the actual external sd card should be:

/storage/extSdCard

The following are several posts regarding to this issues which may help you:

Building a Utility to get the path to External Removable storage every time

Check if the SDCard is present, boolean is always true

How could i get the correct external storage on Samsung and all other devices?

Android how to use Environment.getExternalStorageDirectory()

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top