<commit_before>### Xamarin.Android: Use your removable SD card
Demonstrates how to how to access the 'external', __REMOVABLE__ SD card plugged into your Android device.

The code example is done in C#, but exactly the same principles apply in Java, so it should be extremely easy for an Android Java dev to understand exactly what's going on.

### Background
There's been a lot of questions on StackOverflow (and other places) about how to access the 'External Storage' on an Android device. Most people seem to want to know how to read from (and write to) __the SD card__ the've plugged into the SD card slot in their device. 

This sample project shows you how to do just that.

_Sadly, due to the gamification and 'first post!' mentality of some SO posts, a lot of developers go charging off, half-cocked, and answer the question they think is being asked, instead of the one actually being asked. That's why there's so many duplicates of this type of question. And so most of the answers appear to get the concept of 'External' and 'Removable' mixed up, and then start preaching about how the 'External Storage' isn't actually EXTERNAL storage, it's just storage separate from the inaccessible, INTERNAL storage of the device. And sometimes that maybe the questioner is being a moron for getting the two concepts confused, and this may be a duplicate of that question over there. So most of those answers are essentially identical in implementation, and completely have the wrong end of the stick. Or the wrong stick._

Just about all developers who build for Android understand that the 'External' storage does NOT refer to the removable SD card, even tho the file system sometimes refers to it as such. For the purposes of this post, I am going to refer to this as the 'removable' storage, and it will refer to a removable SD card, which a person has physically plugged into the device. 

### Android is Linux-based
Most developers building for Android are also aware, on some level, that the operating system underneath the Dalvik VM is a Linux based one. 

That means that even if there's not a direct Xamarin or Android call to do what you need to do, there may sometimes be a more Linux-y way of doing it.

The first question we've got is: how do we determine whether we even have an removable SD card plugged into the device? 

This is both simultaneously easy to answer, and hard to solve. Easy, because we can make a single call to see what file systems we have mounted. And hard, because those damned device manufacturers haven't seen fit to settle on a standardised naming convention for a _removable_ SD card which is plugged into the device. 

To make matters worse, some manufacturers have hijacked the name of the _internal_ storage, and often called it something stupid like `sdcard0`.

On most Linux systems, there's a file in the `/proc/` directory called `mounts`, which contains a (sometimes very long) list of all the file systems mounted on the device. 

It's a text file. And it doesn't require any special user permissions or device rooting to read it (though writing to it is another matter!).

Here's an example, from my ye anciente Samsung Galaxy S2, which has an SD card slot in it, and a micro-SD card plugged into it (the list is shortened substantially):

```text
rootfs / rootfs ro,relatime 0 0
tmpfs /dev tmpfs rw,nosuid,relatime,mode=755 0 0
devpts /dev/pts devpts rw,relatime,mode=600 0 0
blah blah more mounted stuff blah
:
/dev/block/mmcblk0p10 /data ext4 rw,nosuid,nodev,noatime,barrier=1,
/dev/block/mmcblk0p4 /mnt/.lfs j4fs rw,relatime 0 0
/dev/block/vold/259:3 /storage/sdcard0 vfat rw,dirsync,nosuid,nodev ...
/dev/block/vold/179:9 /storage/extSdCard vfat rw,dirsync,nosuid,nodev ...
:
```

Notice the two last lines there. Both of those are tagged as 'sdcard', and we know one of them has to be our removable one. My money is on `extSdCard`...

Can you see where we're going with this? Good.

### Reading the File System
Remember to turn on `READ_EXTERNAL_STORAGE` and `WRITE_EXTERNAL_STORAGE` permissions in your `Manifest.xml`, or you won't get the results you expect.

Now we can do a simple call to `System.IO.File.ReadAllText("/proc/mounts")` to read the text out of this file, and store it in a string. We can then parse the string to look for things like  `storage`, `sdcard`, `vfat`, `ext` and anything else we think would be a good indicator. And if we can find __all of that on one file-system line__, then the file system mounted there is very likely a good candidate for our _removable_ SD card.

So, after some hacky string parsing (because quick-and-dirty-hacks are how we roll) from the example in this repo:

```csharp

var candidateProcMountEntries = procMounts.Split('\n', '\r').ToList();
candidateProcMountEntries.RemoveAll(s => s.IndexOf("storage", StringComparison.OrdinalIgnoreCase) < 0);
var bestCandidate = candidateProcMountEntries
  .FirstOrDefault(s => s.IndexOf("ext", StringComparison.OrdinalIgnoreCase) >= 0
                       && s.IndexOf("sd", StringComparison.OrdinalIgnoreCase) >= 0
                       && s.IndexOf("vfat", StringComparison.OrdinalIgnoreCase) >= 0);

// e.g. /dev/block/vold/179:9 /storage/extSdCard vfat rw,dirsync,nosuid, blah
if (!string.IsNullOrWhiteSpace(bestCandidate))
{
  var sdCardEntries = bestCandidate.Split(' ');
  sdCardEntry = sdCardEntries.FirstOrDefault(s => s.IndexOf("/storage/", System.StringComparison.OrdinalIgnoreCase) >= 0);
  return !string.IsNullOrWhiteSpace(sdCardEntry) ? string.Format("{0}", sdCardEntry) : string.Empty;
}

```

...we end up with `/storage/extSdCard`.

It's quite likely that this may not be quite good enough, because the manufacturers of an HTC or Xiaomi phone might have called it something different (for example if they'd mounted it at `/storage/bob` we'd be pretty much screwed). But that being said, they can't be too free-spirited with it, or the Android OS wouldn't be able to identify it either, and you wouldn't be able to copy your music or movies to it, and then play them. 

OK, so now we've established how to locate the SD card (assuming we have one plugged in!). 

### Can We Write To It?
How do we know if it's writeable? Well, let's just try and write to it...


```csharp
public static bool IsWriteable(string pathToTest)
{
  bool result = false;
  const string someTestText = "some test text";
  try
  {
    string testFile = string.Format("{0}/{1}.txt", pathToTest, Guid.NewGuid());
    System.IO.File.WriteAllText(testFile, someTestText);
    System.IO.File.Delete(testFile);
    result = true;
  }
  catch (Exception ex) // argh! did we do something stupid? no? then it's not writeable
  {
    Log.Error("ExternalSDStorageHelper", string.Format("Exception: {0}\r\nMessage: {1}\r\nStack Trace: {2}", ex, ex.Message, ex.StackTrace));
  }
  return result;
}

```

_You can create directories off this path to the SD card now too, now that you have it. Exactly the same way you programmatically create them everywhere else_

OK, that works. But this is on an Android 4.1.2 device (not a KitKat one). And it works all the way up to, and including, Android 4.3 (level 18) We'll get to the KitKat part later. For now, it's OK, we have a removable SD card, and we can write to it. Hooray!

### How Much Space Have We Got?
We're not quite done yet though. The standard Xamarin/Android calls for determining how much total/available/usable/free space is available are limited to only those mount points that Android would let you access. Like the inappropriately and stupidly named `ExternalStorageDirectory`, which isn't what we want.

Back to Linux we go. There's a `statvfs()` call we can make to get some basic info about the mounted file system. But luckily for us, there's an object called `Android.OS.StatFS` which wraps that call for us, which makes it really easy to get to.

I'm interested in total space, available space and free space (the last two aren't always interchangeable), so I made an object called `FileSystemBlockInfo`, which I'll be using to store these bits of information

```csharp
  public class FileSystemBlockInfo
  {
    /// <summary>
    /// The path you asked to check file allocation blocks for
    /// </summary>
    public string Path { get; set; }

    /// <summary>
    /// The file system block size, in bytes, for the given path
    /// </summary>
    public double BlockSizeBytes { get; set; }

    /// <summary>
    /// Total size of the file system at the given path
    /// </summary>
    public double TotalSizeBytes { get; set; }

    /// <summary>
    /// Available size of the file system at the given path
    /// </summary>
    public double AvailableSizeBytes { get; set; }

    /// <summary>
    /// Total free size of the file system at the given path
    /// </summary>
    public double FreeSizeBytes { get; set; }
  }
```

Nothing special. Moving on... 

Using the StatFS object in `ExternalSdStorageHelper.GetFileSystemBlockInfo()`

```csharp
    public static FileSystemBlockInfo GetFileSystemBlockInfo(string path)
    {
      var statFs = new StatFs(path);
      var fsbi = new FileSystemBlockInfo();
      if (Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.JellyBeanMr2)
      {
        fsbi.Path = path;
        fsbi.BlockSizeBytes = statFs.BlockSizeLong;
        fsbi.TotalSizeBytes = statFs.BlockCountLong*statFs.BlockSizeLong;
        fsbi.AvailableSizeBytes = statFs.AvailableBlocksLong*statFs.BlockSizeLong;
        fsbi.FreeSizeBytes = statFs.FreeBlocksLong*statFs.BlockSizeLong;
      }
      else // this was deprecated in API level 18 (Android 4.3), so if your device is below level 18, this is what will be used instead.
      {
        fsbi.Path = path;
        // you may want to disable warning about obsoletes, earlier versions of Android are using the deprecated versions
        fsbi.BlockSizeBytes = (long)statFs.BlockSize;
        fsbi.TotalSizeBytes = (long)statFs.BlockCount * (long)statFs.BlockSize;
        fsbi.FreeSizeBytes = (long)statFs.FreeBlocks * (long)statFs.BlockSize;
        fsbi.AvailableSizeBytes = (long) statFs.AvailableBlocks * (long)statFs.BlockSize;
      }
      return fsbi;
    }
```

_We have to do the check for the Android version, because the OS calls being made have been deprecated for older versions. The 'old style', pre Android level 18 didn't use the `Long` suffixes, so if you try and call use those on anything below Android 4.3, it'll crash on you, telling you that that those methods are unavailable. Viva fragmentation!_

And that gives us the info we need to be able to track how much space we have, and have available to play with.

![screenshot](https://github.com/wislon/xamarin-android-use-removable-sd-card/blob/master/res/screenshot.png)

The example solution in this repo ties this all together, with a simple UI to show the mount point of a removable SD card (as pertains to my SGS I, SGS II and a couple of other devices I've tried). I hope it's of use to someone. And if you find any funky-named removable SD card mounts which the hacky parsing we did above wouldn't have caught, please add it, and send through pull request! :)

### Damn You Android KitKat!
One caveat: __Android KitKat (aka 4.4, aka level 19, and presumably higher)__ won't let you __write__ to wherever you like on this removable disk. Ironically, earlier, more primitive versions of Android do. 

### Google Y U No Allow Access To SD Cards in KitKat?!
The general consensus seems to be for security reasons. Basically stopping random or rogue apps from writing to places they don't own. There are some patches and apps which can fix this for you, but they all appear to require rooting your device. KitKat doesn't block you completely from writing to the SD card, but it means you have to make sure your data is stored somewhere "private" to the app. This will be someplace like [externalSdpath]/Android/data/your.package.name/files/yourFolderName.

There's some more information on [Google](https://www.google.com/search?q=android+kitkat+SD+card), some apps [here](https://play.google.com/store/apps/details?id=com.geeksoft.extcardpatch&hl=en) and [here](https://play.google.com/store/apps/details?id=nextapp.sdfix&hl=en) which may help you figure out what you'll need to do to make it work. And there's some explanations of what other app developers have done to 'fix' their apps over here at [BeyondPod](http://www.beyondpod.com/Android/help/FAQKitKatSDReadOnly.htm) and [doubleTwist](https://www.doubletwist.com/help/question/what-are-the-changes-to-sd-card-support-in-android-44-kitkat/). 
Unfortunately, it looks like your users will now also lose whatever data your app has generated if you put your data in these sandboxed folders (though app upgrades are fine).

_Note: I'm not affiliated with any of these devs or apps_

_Everything in here is released as OSS under the MIT license, so feel free to use it any way you like._<commit_msg>shorten readme to just reference the blog posts<commit_after>### Xamarin.Android: Use your removable SD card (KitKat+ too!)
Demonstrates how to how to access the 'external', __REMOVABLE__ SD card plugged into your Android device.

The code example is done in C#, but exactly the same principles apply in Java, so it should be extremely easy for an Android Java dev to understand exactly what's going on.

### Background
There's been a lot of questions on StackOverflow (and other places) about how to access the 'External Storage' on an Android device. Most people seem to want to know how to read from (and write to) __the SD card__ the've plugged into the SD card slot in their device. 

This sample project shows you how to do just that.

For more details, there's two blog posts about this that I've put together;

* [http://blog.wislon.io/posts/2014/09/28/xamarin-and-android-how-to-use-your-external-removable-sd-card](http://blog.wislon.io/posts/2014/09/28/xamarin-and-android-how-to-use-your-external-removable-sd-card) - how to figure out where it is, and access it pre-KitKat
* [http://blog.wislon.io/posts/2014/11/20/xamarin-and-android-kitkat-and-your-external-sd-card](http://blog.wislon.io/posts/2014/11/20/xamarin-and-android-kitkat-and-your-external-sd-card) - how to get access to it in KitKat and above, with some notes about backward compatibility.

_Everything in here is released as OSS under the MIT license, so feel free to use it any way you like._
{'fraction_non_alphanumeric': 0.07852129803349316, 'fraction_numerical': 0.007226738934056007, 'mean_word_length': 3.9764868603042878, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 21, 'https://': 5, 'lorem ipsum': 0, 'www.': 3, 'xml': 1}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7401033', 'n_tokens_mistral': 4193, 'n_tokens_neox': 3938, 'n_words': 1950}