The Storage Situation: Internal Storage
Back in 2014, I wrote a series of blog posts to try to clear up confusion around where you can read and write files in Android. I updated them in 2017 to reflect changes in Android… but Android keeps changing.
This post is an updated edition of the 2017 post on internal storage.
Working with files on the filesystem in Android is seriously confusing. It was confusing in 2008, and it has only gotten more confusing as the years rolled on. There are countless Stack Overflow 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 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
Some of your users will not recognize “internal” as a qualifier on “storage”. At best, they will think that “internal storage” represents all of the on-board flash. For example, users may still see “internal storage” in places like an Explorer window in Windows, when their device is connected via USB.
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, they never actually bother to define the term.
In truth, the Android SDK’s definition of “internal storage” is a specific directory, unique to your app, where your app can place files. Those files are read-write for your app, but no other apps will have access to those files.
(exception: users running file managers with superuser privileges on rooted devices can access anything)
There are a handful of methods on Context
that give you access to
particular locations on 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 ID, as is
declared in the package
attribute 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 not a good idea.
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, we have the notion of separate user profiles, starting in Android 4.2 for tablets and Android 5.0 for phones. 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
The Device File Explorer tool in Android Studio 3.0+ can browse all of internal storage on an emulator, plus internal storage of debuggable apps on production devices. This should handle most of your scenarios:
From the command line, you can 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 ofcp
on older devices
Once the file is on external storage, you can use adb pull
to download
it to your development machine, or access it by other conventional means
(e.g., via mounting the device as a drive on your development machine).
Limitations of Internal Storage
On ancient 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. We also had onions on our belts, as that was the style at the time.)
Nowadays, internal storage usually is substantially larger: 32GB, 64GB, 128GB, and so on.
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-Writable?
No. That is no longer an option on modern versions of Android, and it was never a particularly good idea.
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 simultaneous 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 available resource, just like you treat your own Web service.
Besides, android:sharedUserId
was deprecated in Android 10 and is slated to be removed
from the OS in a future release.
How Do I Prevent Users of Rooted Devices From Accessing My Files on Internal Storage?
Simple: don’t put those 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.
Did Any of Those Android 10 Changes Affect Internal Storage?
No. You can work with internal storage on Android 10 the same as you have in other recent Android versions.
Should I Hardcode Paths in My App?
No.
Other Posts in This Series
This series includes:
- This post on internal storage
- A post on external storage
- A post on removable storage