The Storage Situation: Internal Storage

UPDATE 2017-11-13: This is the older edition of this blog post — I suggest that you click through and read the newer one.

There is a lot of confusion regarding Android’s storage model. That confusion has only increased with Android 4.4’s changes to that storage model. There are countless StackOverflow questions and the like where they clearly do not quite grok the various pieces of Android’s storage model.

This is the first post in a five-part series covering this storage model, to help clarify what is going on. Today, we will look at “internal storage”… including its various definitions.

What Your Users Think “Internal Storage” Means

Your users will eventually wander into Settings > Storage on their device and see a screen that describes “Internal Storage”:

Internal Storage in Android Settings, Nexus 4, Android 4.4.2

And that will be that. The user thinks that all of the on-board flash is “internal storage”.

What Google Thinks “Internal Storage” Means

Alas, that is not what the Android SDK thinks “internal storage” means, which is where some of the confusion lies.

If you read the Android documentation on internal storage, the description is… hand-wavy:

You can save files directly on the device’s internal storage. By default, files saved to the internal storage are private to your application and other applications cannot access them (nor can the user). When the user uninstalls your application, these files are removed.

In truth, the Android SDK’s definition of “internal storage” is a specific directory, unique to your app, where your app can place files. As suggested in the docs, those files by default are read-write for your app and deny-all for any other app.

(exception: users running file managers with superuser privileges on rooted devices can access anything)

There are a handful of base methods on Context that give you access to internal storage, including:

  • getCacheDir()
  • getDir()
  • getDatabasePath()
  • getFilesDir()
  • openFileInput()
  • openFileOutput()

Other methods will rely upon these, such as openOrCreateDatabase(). Other classes also will rely upon these, such as SQLiteOpenHelper and SharedPreferences.

Where Internal Storage Is Stored… Sometimes

If you look around various blog posts, StackOverflow answers, and books that came out in 2012 or earlier, you will be told that your app’s “internal storage” resides at:

/data/data/your.application.package.name

(where your.application.package.name is replaced by your application’s package name, as is declared in the manifest or modified via Gradle)

Inside of there will be some directories automatically created by Android as you use some of those Context methods. For example, getFilesDir() returns a File object that points to a files/ directory inside of your app’s internal storage.

Where Internal Storage Is Stored… The Rest of the Time

However, this is not always where your app’s internal storage resides. If there is one rule for developers that you should take away from this blog post series, it is:

NEVER HARDCODE PATHS

Every now and then, I will see developers do something like this:

File f=new File("/data/data/their.app.package.name/files/foo.txt");

This is really really dumb.

First, it is more typing than using getFilesDir():

File f=new File(getFilesDir(), "foo.txt");

More importantly, internal storage is not always at the same place. Notably, on Android tablets, we have the notion of separate user profiles, starting in Android 4.2. Each user gets his or her own “internal storage” area. While the aforementioned directory is still used for the primary user, that is not guaranteed, and it will not be used for secondary accounts.

Poking Around Internal Storage

On an emulator, DDMS has the ability to browse all of internal storage, so you can access file from your app or any other app.

On a rooted device, there are recipes for allowing DDMS the same degree of freedom.

On ordinary (un-rooted) devices, DDMS has no ability to access internal storage. The piece of software on the device that DDMS talks to runs as an ordinary user account on hardware, whereas it runs with superuser privileges on an emulator. The ordinary user on hardware has no ability to get to your app’s files, any more than does any user account associated with any other app.

The recipe for getting at internal storage on hardware is to use the run-as option with adb at the command line. For example, to download a database from the primary user’s internal storage to your development machine, you might use:

adb shell 'run-as your.application.package.name cp /data/data/your.application.package.name/databases/dbname.db /sdcard

Note that:

  • You will need to change the destination to wherever on your device external storage is mapped to (shown here as /sdcard/, which will not work on all devices)

  • You may need to use cat instead of cp on older devices

But, once you run the command, the database will be copied to external storage, which you can access via DDMS, or via any conventional way to get files on and off a device via USB cable (e.g., drive letter in Windows).

Limitations of Internal Storage

On Android 1.x and 2.x devices, internal storage was usually on a dedicated filesystem partition, and that partition was usually rather tiny. The HTC Dream (a.k.a., T-Mobile G1), the original Android device, had a whopping 70MB of internal storage for use by all apps.

(And, no, that’s not a typo. We measured storage in megabytes back then.)

By the time the 2.3 devices were rolling out, internal storage might be as big as 1GB.

Android 3.0 changed the storage model around, in such a fashion as internal storage got more space. Devices that advertise as having 4GB, 8GB, 16GB, etc. of storage space usually had all of that (less existing contents) available for internal storage. We will get into what changed in Android 3.0 and its impacts on the storage model as part of tomorrow’s blog post on external storage.

For Android 1.x and 2.x, internal storage was only really for small files, and you needed to use external storage for everything else. Android 3.0+ means that for most devices and most users, internal storage is fine for files that are not meant to be used routinely by other apps or accessible by the user independently from your app. Some power users, though, will find that even on-board flash is insufficient for what they want to store, so they turn to removable storage… which is a whole ‘nuther can of worms that we will get into later in the week.

Frequently-Asked Questions

Here are some FAQs about what the SDK refers to as internal storage:

Should I Make Files on Internal Storage World-Readable or World-Writeable?

Oh, $DEITY, no. Use FileProvider and serve that content via that ContentProvider implementation. Then, you at least have the option of using Android’s permission system to manage access to those files, versus having any app on the system be able to monkey with those files.

Well, How About android:sharedUserId?

I also counsel against this.

android:sharedUserId is an attribute you can place in your manifest that indicates a logical user ID to be used for your app. Any other app that is installed that is signed by the same signing key and requests the same android:sharedUserId will use the same Linux user from a security standpoint. The net effect is that those two apps will be able to work with each other’s files with impunity, as those files are all owned by the same Linux user.

This attribute is really designed for pre-installed apps, such as some software suite pre-loaded by the device manufacturer, carrier, or ROM mod maintainer. In particular, once you ship your app, you cannot reliably change your android:sharedUserId value without locking your user out of any existing files… as Android does not change the ownership of existing files when it changes the Linux user account that your app runs as.

There are various risks in having multiple processes work simultaneously with files. Some subsystems, like SQLite, have built-in logic to deal with this. But if you are doing your own file access yourself (e.g., via File and Java I/O), then you have to somehow deal with simultaenous access, which can get tricky.

You also have to deal with what happens when one app is uninstalled, taking away files that another app was using. In a hub-and-spoke model, like an app and a suite of plugins, perhaps this is not that risky. In other models, where apps are closer to being peers, you cannot afford to have your app’s data vanish because the user elected to uninstall some separate app.

Finally, you do not know what the future might bring. Right now, you might view your set of apps as a tightly-coupled suite. Somebody who acquires those apps, or acquires your firm, might wish to go a different route. Using data sharing options that are more loosely coupled, like a ContentProvider, gives you greater flexibility than does assuming that your apps should co-mingle their files. In an ideal world, your app should treat other apps as a fairly-reliable but not always avaialble resource, just like you treat your own Web service.

How Do I Prevent Users of Rooted Devices From Accessing My Files on Internal Storage?

Simple: don’t put files on internal storage. Users of rooted devices can access whatever they want on the device, so the only way to prevent them from accessing your data is to not have it on the device.

Some developers will attempt to encrypt their files, using a hard-coded password, to prevent rooted device users from using those files. This does erect a “speed bump”, but it is a small one. All it takes is one person with interest to reverse-engineer your app, determine how to decrypt those files, then write up a blog post or discussion board entry on how to do it.

On the whole, there are relatively few people with rooted devices – I estimate it at well under 1%. IMHO, you will have better success by focusing your engineering work on writing a better app, rather than spending that time trying to block rooted device users.

The Rest of the Posts

The entire blog post series covers: