How can I get the external SD card path for Android 4.0+?
Asked Answered
I

27

95

Samsung Galaxy S3 has an external SD card slot, which is mounted to /mnt/extSdCard.

How can I get this path by something like Environment.getExternalStorageDirectory()?

This will return mnt/sdcard, and I can't find the API for the external SD card. (Or removable USB storage on some tablets.)

Instep answered 1/7, 2012 at 9:48 Comment(5)
Why do you want this? It is very inadvisable on Android to do things like this. Perhaps if you share your motivation we can point you in the direction of the best practice for this type of thing.Davide
When your user change their phone, plug the SD card to new phone, and on first phone it's /sdcard, on second phone it's /mnt/extSdCard, anything using file path is crashed. I need to generate real path from its relative path.Ritter
I now this topic is old but this may help. you should use this method. System.getenv(); see project Environment3 to access all storage that are connected to your device. github.com/omidfaraji/Environment3Armagnac
Possible duplicate of Find an external SD card locationMadera
Here's my solution which works till Nougat: https://mcmap.net/q/128185/-how-to-get-sd_card-path-in-android6-0-programmaticallyFormularize
S
59

I have a variation on a solution I found here

public static HashSet<String> getExternalMounts() {
    final HashSet<String> out = new HashSet<String>();
    String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
    String s = "";
    try {
        final Process process = new ProcessBuilder().command("mount")
                .redirectErrorStream(true).start();
        process.waitFor();
        final InputStream is = process.getInputStream();
        final byte[] buffer = new byte[1024];
        while (is.read(buffer) != -1) {
            s = s + new String(buffer);
        }
        is.close();
    } catch (final Exception e) {
        e.printStackTrace();
    }

    // parse output
    final String[] lines = s.split("\n");
    for (String line : lines) {
        if (!line.toLowerCase(Locale.US).contains("asec")) {
            if (line.matches(reg)) {
                String[] parts = line.split(" ");
                for (String part : parts) {
                    if (part.startsWith("/"))
                        if (!part.toLowerCase(Locale.US).contains("vold"))
                            out.add(part);
                }
            }
        }
    }
    return out;
}

The original method was tested and worked with

  • Huawei X3 (stock)
  • Galaxy S2 (stock)
  • Galaxy S3 (stock)

I'm not certain which android version these were on when they were tested.

I've tested my modified version with

  • Moto Xoom 4.1.2 (stock)
  • Galaxy Nexus (cyanogenmod 10) using an otg cable
  • HTC Incredible (cyanogenmod 7.2) this returned both the internal and external. This device is kinda an oddball in that its internal largely goes unused as getExternalStorage() returns a path to the sdcard instead.

and some single storage devices that use an sdcard as their main storage

  • HTC G1 (cyanogenmod 6.1)
  • HTC G1 (stock)
  • HTC Vision/G2 (stock)

Excepting the Incredible all these devices only returned their removable storage. There are probably some extra checks I should be doing, but this is at least a bit better than any solution I've found thus far.

Spoiler answered 30/11, 2012 at 16:13 Comment(14)
I think you solution will give invalid folder name if original one contains capital letters like /mnt/SdCardBurke
I don't believe any of android's file systems are case sensitive. Though I could be wrong about that.Spoiler
Updated solution to make it handle case better. Thanks for pointing that out.Spoiler
I've tested on some Motorola, Samsung and LG devices and it worked perfect.Branson
Did NOT worked for USB devices attached through OTG cable on Nexus 5 and Nexus 7.Hiller
@KhawarRaza, this method stopped working as of kitkat. Use Dmitriy Lozenko's method, with this as a fallback if you are supporting pre-ics devices.Spoiler
What aspect of it stopped working as of kitkat... launching a "mount" process? Relying on "vold" in the 'device' column?Nabal
Regarding case sensitivity, the docs source.android.com/devices/storage say "Android supports devices with external storage, which is defined to be a case-insensitive filesystem with immutable POSIX permission classes and modes." So according to that, all external storage is case-insensitive.Nabal
On new Samsung tablets and HTC One I can find path to external microsd but haven't permission to write there. Did you faced with such a problem?Futurism
Tested on HTC One SV (android 4.2.2) and works. As pointed by @Spoiler this should be used for android pre-kitkat because of the way permissions to write to secondary sdcard changed starting with KitKat.Zig
Prompt me, please, and how to use it?Monadnock
this is returning /mnt instead of /storage on 4.0+ for me on a Galaxy Note 3Doolie
This isn't working on a Samsung Galaxy Tab E with an encrypted microSD card inserted. The path that it finds (/mnt/secure/asec) exists but is not readable by the application. After a bit of digging, it appears the card is accessible through /storage/6435-3633 (I have no idea what those numbers mean or where they came from.)Gravestone
Should you use sdfat as well?Carpet
P
54

I found more reliable way to get paths to all SD-CARDs in system. This works on all Android versions and return paths to all storages (include emulated).

Works correctly on all my devices.

P.S.: Based on source code of Environment class.

private static final Pattern DIR_SEPORATOR = Pattern.compile("/");

/**
 * Raturns all available SD-Cards in the system (include emulated)
 *
 * Warning: Hack! Based on Android source code of version 4.3 (API 18)
 * Because there is no standart way to get it.
 * TODO: Test on future Android versions 4.4+
 *
 * @return paths to all available SD-Cards in the system (include emulated)
 */
public static String[] getStorageDirectories()
{
    // Final set of paths
    final Set<String> rv = new HashSet<String>();
    // Primary physical SD-CARD (not emulated)
    final String rawExternalStorage = System.getenv("EXTERNAL_STORAGE");
    // All Secondary SD-CARDs (all exclude primary) separated by ":"
    final String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE");
    // Primary emulated SD-CARD
    final String rawEmulatedStorageTarget = System.getenv("EMULATED_STORAGE_TARGET");
    if(TextUtils.isEmpty(rawEmulatedStorageTarget))
    {
        // Device has physical external storage; use plain paths.
        if(TextUtils.isEmpty(rawExternalStorage))
        {
            // EXTERNAL_STORAGE undefined; falling back to default.
            rv.add("/storage/sdcard0");
        }
        else
        {
            rv.add(rawExternalStorage);
        }
    }
    else
    {
        // Device has emulated storage; external storage paths should have
        // userId burned into them.
        final String rawUserId;
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)
        {
            rawUserId = "";
        }
        else
        {
            final String path = Environment.getExternalStorageDirectory().getAbsolutePath();
            final String[] folders = DIR_SEPORATOR.split(path);
            final String lastFolder = folders[folders.length - 1];
            boolean isDigit = false;
            try
            {
                Integer.valueOf(lastFolder);
                isDigit = true;
            }
            catch(NumberFormatException ignored)
            {
            }
            rawUserId = isDigit ? lastFolder : "";
        }
        // /storage/emulated/0[1,2,...]
        if(TextUtils.isEmpty(rawUserId))
        {
            rv.add(rawEmulatedStorageTarget);
        }
        else
        {
            rv.add(rawEmulatedStorageTarget + File.separator + rawUserId);
        }
    }
    // Add all secondary storages
    if(!TextUtils.isEmpty(rawSecondaryStoragesStr))
    {
        // All Secondary SD-CARDs splited into array
        final String[] rawSecondaryStorages = rawSecondaryStoragesStr.split(File.pathSeparator);
        Collections.addAll(rv, rawSecondaryStorages);
    }
    return rv.toArray(new String[rv.size()]);
}
Pathognomy answered 18/9, 2013 at 11:29 Comment(8)
What was DIR_SEPORATOR suppose to be?Rebutter
@Rebutter its provided on top of code.. its a Pattern for /Vouch
Anyone tested this solution on Lolipop?Bryology
I tried this method on lenovo P780.It is not working.Banff
Works up to Lollipop; not working on Marshmallow (Galaxy Tab A w/ 6.0.1) where System.getenv("SECONDARY_STORAGE") returns /storage/sdcard1. That location doesn't exist, but actual location is /storage/42CE-FD0A, where 42CE-FD0A is the volume serial number of the formatted partition.Xray
@Xray I checked on Asus Zenfone2, Marshmallow 6.0.1, it's fail to get the micro sd path /storage/F99C-10F4/, how did you solve the problem?Hesitancy
I have to edit the original code to solve the problem https://mcmap.net/q/128186/-how-can-i-get-the-external-sd-card-path-for-android-4-0Hesitancy
On Android 26 Pixel 1 Emulator it doesn't return SD Card (removable), only apps like X-plore and Solid Explorer can see this card and default Files appOutpour
A
32

I guess to use the external sdcard you need to use this:

new File("/mnt/external_sd/")

OR

new File("/mnt/extSdCard/")

in your case...

in replace of Environment.getExternalStorageDirectory()

Works for me. You should check whats in the directory mnt first and work from there..


You should use some type of selection method to choose which sdcard to use:

File storageDir = new File("/mnt/");
if(storageDir.isDirectory()){
    String[] dirList = storageDir.list();
    //TODO some type of selecton method?
}
Asti answered 1/7, 2012 at 9:53 Comment(5)
Well in fact I want a method rather than hard-code path, but the /mnt/ list should be fine. Thank you.Ritter
No problem. Have a option in settings which you choose the path from, then use the method to retrieve this.Asti
Unfortunately the external SD card may be in a different folder than /mnt. E.g. on Samsung GT-I9305 it's /storage/extSdCard while the in-built SD card has the path /storage/sdcard0Augmentative
Well then you would get the sdcard location, then get its parent directory.Asti
What will be the path of External SD card ( for pendrive connected to tablet via OTG cable) for Android 4.0+?Doctorate
H
18

I was using Dmitriy Lozenko's solution until i checked on an Asus Zenfone2, Marshmallow 6.0.1 and the solution is not working. The solution failed when getting EMULATED_STORAGE_TARGET, specifically for microSD path, i.e: /storage/F99C-10F4/. I edited the code to get the emulated root paths directly from emulated application paths with context.getExternalFilesDirs(null); and add more known phone-model-specific physical paths.

To make our life easier, I made a library here. You can use it via gradle, maven, sbt, and leiningen build system.

If you like the old-fashioned way, you can also copy paste the file directly from here, but you will not know if there is an update in the future without checking it manually.

If you have any question or suggestion, please let me know

Hesitancy answered 14/11, 2016 at 5:55 Comment(3)
I haven't used this extensively, but this works well for my problem.Raffia
Great solution! One note however - if SD card is removed when application is active, context.getExternalFilesDirs(null) will return null for one of files and there will be exception in the following loop. I suggest adding if (file == null) continue; as the first line in the loop.Mylan
@Mylan thank you for the suggestion, i have added it and fixed a typo to my answerHesitancy
L
15

In order to retrieve all the External Storages (whether they are SD cards or internal non-removable storages), you can use the following code:

final String state = Environment.getExternalStorageState();

if ( Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state) ) {  // we can read the External Storage...           
    //Retrieve the primary External Storage:
    final File primaryExternalStorage = Environment.getExternalStorageDirectory();

    //Retrieve the External Storages root directory:
    final String externalStorageRootDir;
    if ( (externalStorageRootDir = primaryExternalStorage.getParent()) == null ) {  // no parent...
        Log.d(TAG, "External Storage: " + primaryExternalStorage + "\n");
    }
    else {
        final File externalStorageRoot = new File( externalStorageRootDir );
        final File[] files = externalStorageRoot.listFiles();

        for ( final File file : files ) {
            if ( file.isDirectory() && file.canRead() && (file.listFiles().length > 0) ) {  // it is a real directory (not a USB drive)...
                Log.d(TAG, "External Storage: " + file.getAbsolutePath() + "\n");
            }
        }
    }
}

Alternatively, you might use System.getenv("EXTERNAL_STORAGE") to retrieve the primary External Storage directory (e.g. "/storage/sdcard0") and System.getenv("SECONDARY_STORAGE") to retieve the list of all the secondary directories (e.g. "/storage/extSdCard:/storage/UsbDriveA:/storage/UsbDriveB"). Remember that, also in this case, you might want to filter the list of secondary directories in order to exclude the USB drives.

In any case, please note that using hard-coded paths is always a bad approach (expecially when every manufacturer may change it as pleased).

Lorielorien answered 7/11, 2013 at 8:55 Comment(2)
System.getenv("SECONDARY_STORAGE") did NOT worked for USB devices attached through OTG cable on Nexus 5 and Nexus 7.Hiller
Here's my solution which works till Nougat: https://mcmap.net/q/128185/-how-to-get-sd_card-path-in-android6-0-programmaticallyFormularize
E
13

Good news! In KitKat there's now a public API for interacting with these secondary shared storage devices.

The new Context.getExternalFilesDirs() and Context.getExternalCacheDirs() methods can return multiple paths, including both primary and secondary devices. You can then iterate over them and check Environment.getStorageState() and File.getFreeSpace() to determine the best place to store your files. These methods are also available on ContextCompat in the support-v4 library.

Also note that if you're only interested in using the directories returned by Context, you no longer need the READ_ or WRITE_EXTERNAL_STORAGE permissions. Going forward, you'll always have read/write access to these directories with no additional permissions required.

Apps can also continue working on older devices by end-of-lifing their permission request like this:

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="18" />
Entomo answered 20/3, 2014 at 23:13 Comment(3)
Thank you. I've made some code that follows what you've written, and I think it works well in finding all sd cards paths. Can you take a look ? Here: https://mcmap.net/q/128186/-how-can-i-get-the-external-sd-card-path-for-android-4-0 . BTW, you shouldn't use "getFreeSpace" because theoretically, the free space can change during runtime.Garonne
fore reference developer.android.com/reference/android/content/…Mckown
On Android 26 Pixel 1 Emulator it doesn't return SD Card (removable), only apps like X-plore and Solid Explorer can see this card and default Files appOutpour
S
12

I did the following to get acces to all the external sd cards.

With:

File primaryExtSd=Environment.getExternalStorageDirectory();

you get the path to the primary external SD Then with:

File parentDir=new File(primaryExtSd.getParent());

you get the parent dir of the primary external storage, and it is also the parent of all the external sd. Now, you can list all the storage and select the one that you want.

Hope it is usefull.

Silkaline answered 29/9, 2013 at 2:19 Comment(2)
This should be the accepted answer, and thank you it is useful.Matadi
My sdcard was at /storage/FSAB-2345/. primaryExtSd came as "/storage/emulated/0" but that's actually my internal storage! So I had to do primaryExtSd.getParentFile().getParentFile() to get to "/storage" and that's the parent directory for my external storage directory. It appears a full walk of the tree is needed after getting to the root, to really find all storage.Logan
G
11

Found a new way that is more official starting from Android N (if before, you can try what I've written above) and especially from Android R, using StorageManager (based on a solution I wrote here) :

class MainActivity : AppCompatActivity() {
    @RequiresApi(Build.VERSION_CODES.N)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        getSdCardPaths(this, true)?.forEach { volumePath ->
            Log.d("AppLog", "volumePath:$volumePath")
        }
    }

    /**
     * returns a list of all available sd cards paths, or null if not found.
     *
     * @param includePrimaryExternalStorage set to true if you wish to also include the path of the primary external storage
     */
    fun getSdCardPaths(context: Context, includePrimaryExternalStorage: Boolean): List<String>? {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            val storageManager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
            val storageVolumes = storageManager.storageVolumes
            if (!storageVolumes.isNullOrEmpty()) {
                val primaryVolume = storageManager.primaryStorageVolume
                val result = ArrayList<String>(storageVolumes.size)
                for (storageVolume in storageVolumes) {
                    val volumePath = getVolumePath(storageVolume) ?: continue
                    if (storageVolume.uuid == primaryVolume.uuid || storageVolume.isPrimary) {
                        if (includePrimaryExternalStorage)
                            result.add(volumePath)
                        continue
                    }
                    result.add(volumePath)
                }
                return if (result.isEmpty()) null else result
            }
        }
        val externalCacheDirs = ContextCompat.getExternalCacheDirs(context)
        if (externalCacheDirs.isEmpty())
            return null
        if (externalCacheDirs.size == 1) {
            if (externalCacheDirs[0] == null)
                return null
            val storageState = EnvironmentCompat.getStorageState(externalCacheDirs[0])
            if (Environment.MEDIA_MOUNTED != storageState)
                return null
            if (!includePrimaryExternalStorage && Environment.isExternalStorageEmulated())
                return null
        }
        val result = ArrayList<String>()
        if (externalCacheDirs[0] != null && (includePrimaryExternalStorage || externalCacheDirs.size == 1))
            result.add(getRootOfInnerSdCardFolder(context, externalCacheDirs[0]))
        for (i in 1 until externalCacheDirs.size) {
            val file = externalCacheDirs[i] ?: continue
            val storageState = EnvironmentCompat.getStorageState(file)
            if (Environment.MEDIA_MOUNTED == storageState)
                result.add(getRootOfInnerSdCardFolder(context, externalCacheDirs[i]))
        }
        return if (result.isEmpty()) null else result
    }

    fun getRootOfInnerSdCardFolder(context: Context, inputFile: File): String {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            val storageManager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
            storageManager.getStorageVolume(inputFile)?.let {
                val result = getVolumePath(it)
                if (result != null)
                    return result
            }
        }
        var file: File = inputFile
        val totalSpace = file.totalSpace
        while (true) {
            val parentFile = file.parentFile
            if (parentFile == null || parentFile.totalSpace != totalSpace || !parentFile.canRead())
                return file.absolutePath
            file = parentFile
        }
    }

    @RequiresApi(Build.VERSION_CODES.N)
    fun getVolumePath(storageVolume: StorageVolume): String? {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
            return storageVolume.directory?.absolutePath
        try {
            val storageVolumeClazz = StorageVolume::class.java
            val getPath = storageVolumeClazz.getMethod("getPath")
            return getPath.invoke(storageVolume) as String
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }
}
Garonne answered 28/11, 2014 at 23:38 Comment(7)
externalCacheDirs[0].getParentFile().getParentFile().getParentFile().getParentFile().getAbsolutePath() This is a recipe for disaster. If you're doing this, you might as well just hard code the path you want since, if you get a cache dir with anything other than 4 parents you will get NullPointerException or the wrong path.Davide
@Davide Correct. Fixed this issue too. Please have a look.Garonne
The docs (developer.android.com/reference/android/content/…) say "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." It sounds like they also do not include SD card slots, i.e. the places you can remove an SD card without going under the battery. Hard to tell, though.Nabal
@Nabal Well, I've tested it myself on SGS3 (and real SD-card), so I think it should work on other devices too.Garonne
@Nabal Try the new code I've offered. Maybe will help. :)Garonne
Calling StorageVolume.getPath() by reflection isn't working with targetSdk 30 anymore.Albion
@AlexeyOzerov The code works fine on emulator API 30 and API 31, and there is already a check if the version is from Android R (API 30) which would not use the reflection, so what's the problem exactly?Garonne
M
5

Thanks for the clues provided by you guys, especially @SmartLemon, I got the solution. In case someone else need it, I put my final solution here( to find the first listed external SD card ):

public File getExternalSDCardDirectory()
{
    File innerDir = Environment.getExternalStorageDirectory();
    File rootDir = innerDir.getParentFile();
    File firstExtSdCard = innerDir ;
    File[] files = rootDir.listFiles();
    for (File file : files) {
        if (file.compareTo(innerDir) != 0) {
            firstExtSdCard = file;
            break;
        }
    }
    //Log.i("2", firstExtSdCard.getAbsolutePath().toString());
    return firstExtSdCard;
}

If no external SD card there, then it returns the on board storage. I will use it if the sdcard is not exist, you may need to change it.

Monniemono answered 25/6, 2013 at 5:38 Comment(1)
this throwing null on Android Version 19. rootDir.listFiles() return null. I have test it with nexus 7, emulator and galaxy note.Pirate
P
3

refer to my code, hope helpful for you:

    Runtime runtime = Runtime.getRuntime();
    Process proc = runtime.exec("mount");
    InputStream is = proc.getInputStream();
    InputStreamReader isr = new InputStreamReader(is);
    String line;
    String mount = new String();
    BufferedReader br = new BufferedReader(isr);
    while ((line = br.readLine()) != null) {
        if (line.contains("secure")) continue;
        if (line.contains("asec")) continue;

        if (line.contains("fat")) {//TF card
            String columns[] = line.split(" ");
            if (columns != null && columns.length > 1) {
                mount = mount.concat("*" + columns[1] + "\n");
            }
        } else if (line.contains("fuse")) {//internal storage
            String columns[] = line.split(" ");
            if (columns != null && columns.length > 1) {
                mount = mount.concat(columns[1] + "\n");
            }
        }
    }
    txtView.setText(mount);
Paranoiac answered 17/1, 2013 at 3:3 Comment(0)
X
3

This solution (assembled from other answers to this question) handles the fact (as mentioned by @ono) that System.getenv("SECONDARY_STORAGE") is of no use with Marshmallow.

Tested and working on:

  • Samsung Galaxy Tab 2 (Android 4.1.1 - Stock)
  • Samsung Galaxy Note 8.0 (Android 4.2.2 - Stock)
  • Samsung Galaxy S4 (Android 4.4 - Stock)
  • Samsung Galaxy S4 (Android 5.1.1 - Cyanogenmod)
  • Samsung Galaxy Tab A (Android 6.0.1 - Stock)

    /**
     * Returns all available external SD-Card roots in the system.
     *
     * @return paths to all available external SD-Card roots in the system.
     */
    public static String[] getStorageDirectories() {
        String [] storageDirectories;
        String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE");
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            List<String> results = new ArrayList<String>();
            File[] externalDirs = applicationContext.getExternalFilesDirs(null);
            for (File file : externalDirs) {
                String path = file.getPath().split("/Android")[0];
                if((Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Environment.isExternalStorageRemovable(file))
                        || rawSecondaryStoragesStr != null && rawSecondaryStoragesStr.contains(path)){
                    results.add(path);
                }
            }
            storageDirectories = results.toArray(new String[0]);
        }else{
            final Set<String> rv = new HashSet<String>();
    
            if (!TextUtils.isEmpty(rawSecondaryStoragesStr)) {
                final String[] rawSecondaryStorages = rawSecondaryStoragesStr.split(File.pathSeparator);
                Collections.addAll(rv, rawSecondaryStorages);
            }
            storageDirectories = rv.toArray(new String[rv.size()]);
        }
        return storageDirectories;
    }
    
Xray answered 7/9, 2016 at 14:1 Comment(2)
The condition rawSecondaryStoragesStr.contains(path) can allow the internal storage also to get added; we can better remove that condition.. I faced that issue, since my device returns the internal storage directory withSystem.getenv("SECONDARY_STORAGE"), but returns External Memory Card path with System.getenv(EXTERNAL_STORAGE) on KitKat; it seems different vendors had implemented this differently without any damn standard..Formularize
Here's my solution which works till Nougat: https://mcmap.net/q/128185/-how-to-get-sd_card-path-in-android6-0-programmaticallyFormularize
K
2

Actually in some devices the external sdcard default name is showing as extSdCard and for other it is sdcard1.

This code snippet helps to find out that exact path and helps to retrieve you the path of external device.

String sdpath,sd1path,usbdiskpath,sd0path;    
        if(new File("/storage/extSdCard/").exists())
            {
               sdpath="/storage/extSdCard/";
               Log.i("Sd Cardext Path",sdpath);
            }
        if(new File("/storage/sdcard1/").exists())
         {
              sd1path="/storage/sdcard1/";
              Log.i("Sd Card1 Path",sd1path);
         }
        if(new File("/storage/usbcard1/").exists())
         {
              usbdiskpath="/storage/usbcard1/";
              Log.i("USB Path",usbdiskpath);
         }
        if(new File("/storage/sdcard0/").exists())
         {
              sd0path="/storage/sdcard0/";
              Log.i("Sd Card0 Path",sd0path);
         }
Kirakiran answered 19/5, 2014 at 11:59 Comment(2)
This helped me out immensely.Crosspiece
There are some devices which uses "/storage/external_sd" (LG G3 KitKat), Marshmallow has some different scheme, the Internal Storage on Nexus 5X using Android 6.0 is "/mnt/sdcard" and the external SDcard is found under "/storage/XXXX-XXXX"Liberality
L
2

Yes. Different manufacturer use different SDcard name like in Samsung Tab 3 its extsd, and other samsung devices use sdcard like this different manufacturer use different names.

I had the same requirement as you. so i have created a sample example for you from my project goto this link Android Directory chooser example which uses the androi-dirchooser library. This example detect the SDcard and list all the subfolders and it also detects if the device has morethan one SDcard.

Part of the code looks like this For full example goto the link Android Directory Chooser

/**
* Returns the path to internal storage ex:- /storage/emulated/0
 *
* @return
 */
private String getInternalDirectoryPath() {
return Environment.getExternalStorageDirectory().getAbsolutePath();
 }

/**
 * Returns the SDcard storage path for samsung ex:- /storage/extSdCard
 *
 * @return
 */
    private String getSDcardDirectoryPath() {
    return System.getenv("SECONDARY_STORAGE");
}


 mSdcardLayout.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View view) {
        String sdCardPath;
        /***
         * Null check because user may click on already selected buton before selecting the folder
         * And mSelectedDir may contain some wrong path like when user confirm dialog and swith back again
         */

        if (mSelectedDir != null && !mSelectedDir.getAbsolutePath().contains(System.getenv("SECONDARY_STORAGE"))) {
            mCurrentInternalPath = mSelectedDir.getAbsolutePath();
        } else {
            mCurrentInternalPath = getInternalDirectoryPath();
        }
        if (mCurrentSDcardPath != null) {
            sdCardPath = mCurrentSDcardPath;
        } else {
            sdCardPath = getSDcardDirectoryPath();
        }
        //When there is only one SDcard
        if (sdCardPath != null) {
            if (!sdCardPath.contains(":")) {
                updateButtonColor(STORAGE_EXTERNAL);
                File dir = new File(sdCardPath);
                changeDirectory(dir);
            } else if (sdCardPath.contains(":")) {
                //Multiple Sdcards show root folder and remove the Internal storage from that.
                updateButtonColor(STORAGE_EXTERNAL);
                File dir = new File("/storage");
                changeDirectory(dir);
            }
        } else {
            //In some unknown scenario at least we can list the root folder
            updateButtonColor(STORAGE_EXTERNAL);
            File dir = new File("/storage");
            changeDirectory(dir);
        }


    }
});
Lubra answered 6/9, 2014 at 7:27 Comment(0)
S
1

On some devices (for example samsung galaxy sII )internal memory card mabe be in vfat. In this case use refer last code, we obtain path internal memory card (/mnt/sdcad) but no external card. Code refer below solve this problem.

static String getExternalStorage(){
         String exts =  Environment.getExternalStorageDirectory().getPath();
         try {
            FileReader fr = new FileReader(new File("/proc/mounts"));       
            BufferedReader br = new BufferedReader(fr);
            String sdCard=null;
            String line;
            while((line = br.readLine())!=null){
                if(line.contains("secure") || line.contains("asec")) continue;
            if(line.contains("fat")){
                String[] pars = line.split("\\s");
                if(pars.length<2) continue;
                if(pars[1].equals(exts)) continue;
                sdCard =pars[1]; 
                break;
            }
        }
        fr.close();
        br.close();
        return sdCard;  

     } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return null;
}
Stapleton answered 27/11, 2013 at 7:32 Comment(0)
M
1
       File[] files = null;
    File file = new File("/storage");// /storage/emulated
if (file.exists()) {
        files = file.listFiles();
            }
            if (null != files)
                for (int j = 0; j < files.length; j++) {
                    Log.e(TAG, "" + files[j]);
                    Log.e(TAG, "//--//--// " +             files[j].exists());

                    if (files[j].toString().replaceAll("_", "")
                            .toLowerCase().contains("extsdcard")) {
                        external_path = files[j].toString();
                        break;
                    } else if (files[j].toString().replaceAll("_", "")
                            .toLowerCase()
                            .contains("sdcard".concat(Integer.toString(j)))) {
                        // external_path = files[j].toString();
                    }
                    Log.e(TAG, "--///--///--  " + external_path);
                }
Marmawke answered 13/3, 2014 at 7:51 Comment(0)
D
1

System.getenv("SECONDARY_STORAGE") returns null for Marshmallow. This is another way of finding all the externals dirs. You can check if it's removable which determines if internal/external

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    File[] externalCacheDirs = context.getExternalCacheDirs();
    for (File file : externalCacheDirs) {
        if (Environment.isExternalStorageRemovable(file)) {
            // It's a removable storage
        }
    }
}
Doolie answered 5/7, 2016 at 22:52 Comment(0)
K
0

I am sure this code will surely resolve your issues...This is working fine for me...\

try {
            File mountFile = new File("/proc/mounts");
            usbFoundCount=0;
            sdcardFoundCount=0;
            if(mountFile.exists())
             {
                Scanner usbscanner = new Scanner(mountFile);
                while (usbscanner.hasNext()) {
                    String line = usbscanner.nextLine();
                    if (line.startsWith("/dev/fuse /storage/usbcard1")) {
                        usbFoundCount=1;
                        Log.i("-----USB--------","USB Connected and properly mounted---/dev/fuse /storage/usbcard1" );
                    }
            }
         }
            if(mountFile.exists()){
                Scanner sdcardscanner = new Scanner(mountFile);
                while (sdcardscanner.hasNext()) {
                    String line = sdcardscanner.nextLine();
                    if (line.startsWith("/dev/fuse /storage/sdcard1")) {
                        sdcardFoundCount=1;
                        Log.i("-----USB--------","USB Connected and properly mounted---/dev/fuse /storage/sdcard1" );
                    }
            }
         }
            if(usbFoundCount==1)
            {
                Toast.makeText(context,"USB Connected and properly mounted", 7000).show();
                Log.i("-----USB--------","USB Connected and properly mounted" );
            }
            else
            {
                Toast.makeText(context,"USB not found!!!!", 7000).show();
                Log.i("-----USB--------","USB not found!!!!" );

            }
            if(sdcardFoundCount==1)
            {
                Toast.makeText(context,"SDCard Connected and properly mounted", 7000).show();
                Log.i("-----SDCard--------","SDCard Connected and properly mounted" );
            }
            else
            {
                Toast.makeText(context,"SDCard not found!!!!", 7000).show();
                Log.i("-----SDCard--------","SDCard not found!!!!" );

            }
        }catch (Exception e) {
            e.printStackTrace();
        } 
Kirakiran answered 9/6, 2014 at 7:7 Comment(1)
Environment.getExternalStorageDirectory() should not give you the exact location of your external sdcard or usb location.Just parse to "/proc/mounts" location then /dev/fuse /storage/sdcard1" will give you the location whether your sdcard is properly mounted or not same for usb also.This way you can easily get it..Cheers..SamKirakiran
F
0

To access files in my SD card, on my HTC One X (Android), I use this path:

file:///storage/sdcard0/folder/filename.jpg

Note the tripple "/" !

Federative answered 27/11, 2014 at 1:51 Comment(0)
P
0
 String path = Environment.getExternalStorageDirectory()
                        + File.separator + Environment.DIRECTORY_PICTURES;
                File dir = new File(path);
Plush answered 27/11, 2014 at 22:53 Comment(1)
It doesn't work. It returns path of Internal Storage. i.e. /storage/emulated/0Abfarad
U
0

You can use something like - Context.getExternalCacheDirs() or Context.getExternalFilesDirs() or Context.getObbDirs(). They give application specific directories in all external storage devices where the application can store its files.

So something like this - Context.getExternalCacheDirs()[i].getParentFile().getParentFile().getParentFile().getParent() can get you the root path of external storage devices.

I know these commands are for a different purpose but other answers didn't work for me.

This link gave me good pointers - https://possiblemobile.com/2014/03/android-external-storage/

Unchurch answered 28/6, 2016 at 6:38 Comment(0)
D
0
String secStore = System.getenv("SECONDARY_STORAGE");

File externalsdpath = new File(secStore);

This will get the path of external sd secondary storage.

Detroit answered 10/10, 2016 at 13:41 Comment(0)
C
0

I have tried the solutions provided by Dmitriy Lozenko and Gnathonic on my Samsung Galaxy Tab S2 (Model: T819Y) but none helped me retrieve path to an external SD Card directory. mount command execution contained the required path to external SD Card directory (i.e. /Storage/A5F9-15F4) but it did not match the regular expression hence it was not returned. I don't get the directory naming mechanism followed by Samsung. Why they deviate from standards (i.e. extsdcard) and come up with something really fishy like in my case (i.e. /Storage/A5F9-15F4). Is there anything I am missing? Anyways, following changes in regular expression of Gnathonic's solution helped me get valid sdcard directory:

final HashSet<String> out = new HashSet<String>();
        String reg = "(?i).*(vold|media_rw).*(sdcard|vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
        String s = "";
        try {
            final Process process = new ProcessBuilder().command("mount")
                    .redirectErrorStream(true).start();
            process.waitFor();
            final InputStream is = process.getInputStream();
            final byte[] buffer = new byte[1024];
            while (is.read(buffer) != -1) {
                s = s + new String(buffer);
            }
            is.close();
        } catch (final Exception e) {
            e.printStackTrace();
        }

        // parse output
        final String[] lines = s.split("\n");
        for (String line : lines) {
            if (!line.toLowerCase(Locale.US).contains("asec")) {
                if (line.matches(reg)) {
                    String[] parts = line.split(" ");
                    for (String part : parts) {
                        if (part.startsWith("/"))
                            if (!part.toLowerCase(Locale.US).contains("vold"))
                                out.add(part);
                    }
                }
            }
        }
        return out;

I am not sure if this is a valid solution and if it will give results for other Samsung tablets but it has fixed my problem for now. Following is another method to retrieve removable SD Card path in Android (v6.0). I have tested the method with android marshmallow and it works. Approach used in it is very basic and will surely work for other versions too but testing is mandatory. Some insight into it will be helpful:

public static String getSDCardDirPathForAndroidMarshmallow() {

    File rootDir = null;

    try {
        // Getting external storage directory file
        File innerDir = Environment.getExternalStorageDirectory();

        // Temporarily saving retrieved external storage directory as root
        // directory
        rootDir = innerDir;

        // Splitting path for external storage directory to get its root
        // directory

        String externalStorageDirPath = innerDir.getAbsolutePath();

        if (externalStorageDirPath != null
                && externalStorageDirPath.length() > 1
                && externalStorageDirPath.startsWith("/")) {

            externalStorageDirPath = externalStorageDirPath.substring(1,
                    externalStorageDirPath.length());
        }

        if (externalStorageDirPath != null
                && externalStorageDirPath.endsWith("/")) {

            externalStorageDirPath = externalStorageDirPath.substring(0,
                    externalStorageDirPath.length() - 1);
        }

        String[] pathElements = externalStorageDirPath.split("/");

        for (int i = 0; i < pathElements.length - 1; i++) {

            rootDir = rootDir.getParentFile();
        }

        File[] files = rootDir.listFiles();

        for (File file : files) {
            if (file.exists() && file.compareTo(innerDir) != 0) {

                // Try-catch is implemented to prevent from any IO exception
                try {

                    if (Environment.isExternalStorageRemovable(file)) {
                        return file.getAbsolutePath();

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

            }
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

Kindly share if you have any other approach to handle this issue. Thanks

Champ answered 19/10, 2016 at 5:43 Comment(9)
Here's my solution which works till Nougat: https://mcmap.net/q/128185/-how-to-get-sd_card-path-in-android6-0-programmaticallyFormularize
I actually have the same problem with you on Asus Zenfone 2, but i change the code from Dmitriy Lozenko instead to solve the problem https://mcmap.net/q/128186/-how-can-i-get-the-external-sd-card-path-for-android-4-0Hesitancy
@Hesitancy the solution that you are suggesting might not work with Android Marshmallow(6.0) as the support for environment variables is removed in it. I cannot exactly recall but I think I have given it a try.Champ
@GokulNC I will give it a try. seems legit :)Champ
@GokulNC yes, that is why i use context.getExternalFilesDirs(null); instead of directly use physical paths when the version is >MarshmallowHesitancy
@GokulNC I'm getting following exception while executing your method: java.io.IOException: Error running exec(). Command: [mount | grep /dev/block/vold] Working Directory: null I am testing the implementation on Samsung Galaxy Tab S2 (Model: T819Y).Champ
@Abdul Maybe a shell wasn't obtained to execute the command, or some problem with grep. Anyway, the logic is to obtain the memory card directory from mount command. You could grab a shell by some other method and execute the command and process the output..Formularize
@GokulNC I have also tried using Runtime.getRuntime().exec("mount | grep /dev/block/vold"). The command executes fine but the input stream returned by command is empty. So the issue seems to be with command. Testing was done with the device running Android 6.0.1.Champ
Try in Terminal Emulator app to see what's the output. Also check if the problem is due to grep command, by trying just mount.Formularize
T
0
//manifest file outside the application tag
//please give permission write this 
//<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
        File file = new File("/mnt");
        String[] fileNameList = file.list(); //file names list inside the mnr folder
        String all_names = ""; //for the log information
        String foundedFullNameOfExtCard = ""; // full name of ext card will come here
        boolean isExtCardFounded = false;
        for (String name : fileNameList) {
            if (!isExtCardFounded) {
                isExtCardFounded = name.contains("ext");
                foundedFullNameOfExtCard = name;
            }
            all_names += name + "\n"; // for log
        }
        Log.d("dialog", all_names + foundedFullNameOfExtCard);
Titivate answered 13/4, 2017 at 21:17 Comment(1)
This is alternatively for beginners at least getting stringList of all drivers mounted in device.Titivate
C
0

This is the conclusion of all reliable methods, to work for all android versions (Hopefully).

private static ArrayList<String> getAllStorages(Context context)
{
    ArrayList<String> storagePaths = new ArrayList<>();

    storagePaths.add(Environment.getExternalStorageDirectory().getAbsolutePath());

    try
    {
        File[] externalDirs = context.getExternalFilesDirs(null);
        if (externalDirs != null)
            for (File file : externalDirs)
            {
                storagePaths.add(file.getAbsolutePath());
            }

        externalDirs = context.getExternalMediaDirs();
        if (externalDirs != null)
            for (File file : externalDirs)
            {
                storagePaths.add(file.getAbsolutePath());
            }
    }
    catch (Throwable e)
    {
        // ignore
    }

    try
    {
        StorageManager sm=(StorageManager) context.getSystemService(context.STORAGE_SERVICE);
        for (StorageVolume e:sm.getStorageVolumes())
        {
            String s= (String) StorageVolume.class.getMethod("getPath").invoke(e);
            storagePaths.add(s);
        }
    }
    catch (Throwable e)
    {
        //ignore
    }
    
    for (String key: new String[]{
        "SECONDARY_STORAGE",
        "EXTERNAL_STORAGE",
        "EMULATED_STORAGE_TARGET"
    })
    {
        String externalStorage = System.getenv(key);
        if (externalStorage != null && !externalStorage.isEmpty())
        {
            String[] externalPaths = externalStorage.split(":");
            for (String e:externalPaths)
            {
                storagePaths.add(e);
            }
        }
    }

    ArrayList<String> result=new ArrayList<>();
    for (String s:storagePaths)
    {
        File f=new File(s);
        File f2=f;
        while (f2 != null)
        {
            if (f2.canRead())
            {
                f = f2;
            }
            f2 = f2.getParentFile();
        }
        try
        {
            f = f.getCanonicalFile();
        }
        catch (IOException e)
        {
            // ignore
        }
        s = f.getPath();
        if (!result.contains(s))
        {
            result.add(s);
        }
    }
    return result;
}

Tested on Android 11.

You should test it on each version and comment below if any is not compatible, so can improve it.

Chloromycetin answered 18/9, 2023 at 16:26 Comment(0)
T
-1

that's not true. /mnt/sdcard/external_sd can exist even if the SD card is not mounted. your application will crash when you try to write to /mnt/sdcard/external_sd when it's not mounted.

you need to check if the SD card is mounted first using:

boolean isSDPresent = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
Tracheostomy answered 15/11, 2012 at 10:41 Comment(3)
This is meaningless when getExternalStorageState() returns the internal storage rather than external SD card, I think.Ritter
Do they provide a function to get external SD card now? I can not find any clue after searching around for an hour. If this is the case, then this is so strange after so many versions updated.Monniemono
When you said "that's not true", what were you referring to? I know, that was 2.5 years ago...Nabal
M
-1

On Galaxy S3 Android 4.3 the path I use is ./storage/extSdCard/Card/ and it does the job. Hope it helps,

Mitsue answered 20/12, 2013 at 15:46 Comment(0)
C
-1

The following steps worked for me. You just need to write this lines:

String sdf = new String(Environment.getExternalStorageDirectory().getName());
String sddir = new String(Environment.getExternalStorageDirectory().getPath().replace(sdf,""));

The first line will give the name of sd directory, and you just need to use it in the replace method for the second string. The second string will contain the path for the internal and removable sd(/storage/ in my case). I just needed this path for my app but you can go further if you need it.

Cutie answered 1/3, 2016 at 5:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.