Android application developed in Java to demonstrate secure local data persistence on Android. The lab covers
SharedPreferences,EncryptedSharedPreferences, internal files, cache storage, app-specific external storage, and secure data cleanup.
The objective of this lab is to understand the different forms of local persistence available on Android while applying fundamental security practices.
The application demonstrates:
- writing and reading non-sensitive data with
SharedPreferences; - the difference between
apply()andcommit(); - securely storing a token with
EncryptedSharedPreferences; - using a
MasterKeybased on Android Keystore; - writing and reading UTF-8 internal files;
- saving and loading a local JSON file;
- using the cache directory with
cacheDir; - exporting a file to app-specific external storage;
- completely clearing local application data;
- verifying that secrets are never exposed in plaintext through Logcat.
demo8.mp4
The application contains a single screen with:
- a username field;
- a language list containing
fr,en, andar; - a dark-theme switch;
- a masked token field;
- buttons for saving and loading data;
- buttons for managing JSON files;
- buttons for exporting and reading an app-specific external file;
- a button for completely clearing local data;
- an area for displaying operation results.
| Feature | Description |
|---|---|
| SharedPreferences | Saves the username, language, and theme |
| apply() | Saves preferences asynchronously |
| commit() | Saves preferences synchronously and returns a boolean result |
| EncryptedSharedPreferences | Stores the token in encrypted form |
| MasterKey | Generates an encryption key based on Android Keystore |
| Internal files | Saves note.txt using UTF-8 encoding |
| Local JSON | Saves a list of students in students.json |
| Cache | Writes a temporary last_ui.txt file to cacheDir |
| App-specific external storage | Exports an export.txt file to the application's external directory |
| Data cleanup | Deletes preferences, internal files, cache files, and the external export |
| Security | Never displays the token in plaintext in the interface or Logcat |
app/src/main/java/com/example/securestoragejava/
│
├── ui/
│ └── MainActivity.java
│
├── prefs/
│ ├── AppPrefs.java
│ └── SecurePrefs.java
│
├── files/
│ ├── InternalTextStore.java
│ └── StudentsJsonStore.java
│
├── cache/
│ └── CacheStore.java
│
├── external/
│ └── ExternalAppFilesStore.java
│
└── model/
└── Student.java
| File | Role |
|---|---|
MainActivity.java |
Manages the main interface and user actions |
AppPrefs.java |
Stores non-sensitive preferences |
SecurePrefs.java |
Stores the token using EncryptedSharedPreferences |
InternalTextStore.java |
Reads and writes UTF-8 internal files |
StudentsJsonStore.java |
Saves and loads a list of students in JSON format |
CacheStore.java |
Manages a temporary file in cacheDir |
ExternalAppFilesStore.java |
Exports and reads an app-specific external file |
Student.java |
Model representing a student |
The lab uses AndroidX Security Crypto for encrypted storage:
implementation("androidx.security:security-crypto:1.1.0-alpha06")This dependency provides:
MasterKey;EncryptedSharedPreferences.
| Concept | Usage |
|---|---|
SharedPreferences |
Simple key-value storage |
EncryptedSharedPreferences |
Encrypted key-value storage |
MasterKey |
Keystore-backed encryption key |
MODE_PRIVATE |
Storage private to the application |
openFileOutput() |
Writes data to internal storage |
openFileInput() |
Reads data from internal storage |
cacheDir |
Temporary cache directory |
getExternalFilesDir(null) |
App-specific external storage |
org.json |
Creates and reads JSON data |
Logcat |
Verifies logs for the absence of secret leakage |
| Data | Location | Sensitive? | Protection |
|---|---|---|---|
| Username | SharedPreferences |
No | MODE_PRIVATE |
| Language | SharedPreferences |
No | MODE_PRIVATE |
| Theme | SharedPreferences |
No | MODE_PRIVATE |
| Token | EncryptedSharedPreferences |
Yes | Encryption |
note.txt |
Internal storage | Non-sensitive | MODE_PRIVATE |
students.json |
Internal storage | Non-sensitive | MODE_PRIVATE |
last_ui.txt |
Cache | Temporary | Private cache |
export.txt |
App-specific external storage | Non-sensitive | Application directory |
The following security rules were applied:
- the token is never displayed in plaintext in the interface;
- the token is never written in plaintext to Logcat;
- only the token length is displayed through
tokenLength; - standard preferences use
MODE_PRIVATE; - secrets are stored using
EncryptedSharedPreferences; - internal files remain private to the application;
- the cache is used only for temporary data;
- external exports are limited to the app-specific directory;
- a button allows complete data cleanup;
- exceptions are handled without exposing secrets.
| Test | Expected Result |
|---|---|
| Application startup | The main screen is displayed |
| Save preferences | The username, language, and theme are saved |
| Load preferences | Values are restored from SharedPreferences |
| Save token | The token is stored in encrypted form |
| Load token | Only tokenLength is displayed |
| Save JSON file | students.json and note.txt are created |
| Load JSON file | The note and students are displayed |
| External export | export.txt is created in app-specific external storage |
| Read external export | The content of export.txt is displayed |
| Clear all | Preferences, files, cache, and external export are deleted |
| Logcat | The plaintext token is never leaked |
After running the application, the files can be inspected using Android Studio.
Internal files path:
/data/data/com.example.securestoragejava/files/
Expected files:
note.txt
students.json
Cache path:
/data/data/com.example.securestoragejava/cache/
Expected file:
last_ui.txt
Preferences path:
/data/data/com.example.securestoragejava/shared_prefs/
Expected files:
app_prefs.xml
secure_prefs.xml
App-specific external export path:
/storage/emulated/0/Android/data/com.example.securestoragejava/files/export.txt
Create the following directory:
docs/screenshots/
Add the following screenshots:
docs/screenshots/01_home_screen.png
docs/screenshots/02_save_prefs.png
docs/screenshots/03_load_prefs.png
docs/screenshots/04_logcat_no_token.png
docs/screenshots/05_save_json.png
docs/screenshots/06_load_json.png
docs/screenshots/07_device_file_explorer_files.png
docs/screenshots/08_shared_prefs_secure.png
docs/screenshots/09_external_export.png
docs/screenshots/10_clear_all.png
Example README integration:









git init
git remote add origin https://github.com/bgoussama/SecureStorageLabJava.git
git add .
git commit -m "Add SecureStorageLabJava Android project"
git push -u origin feature/M6-frontendIf the remote already exists:
git remote set-url origin https://github.com/bgoussama/SecureStorageLabJava.git- Clone the repository:
git clone https://github.com/bgoussama/SecureStorageLabJava.git-
Open the project in Android Studio.
-
Synchronize Gradle.
-
Run the application on an emulator or Android device.
-
Test the following features:
- saving preferences;
- loading preferences;
- encrypted token storage;
- saving JSON data;
- exporting to external storage;
- completely clearing local data.
The application works without an Internet connection and demonstrates several Android local persistence mechanisms.
It shows how to store standard preferences, protect a token through encryption, write internal files, manage temporary cache data, export a file to app-specific external storage, and securely clear stored data.
This lab validates the fundamentals of secure local persistence on Android using Java.
It highlights the difference between non-sensitive and sensitive data and demonstrates why tokens must never be stored in plaintext or displayed in Logcat.
The solution follows key security practices: MODE_PRIVATE, EncryptedSharedPreferences, no sensitive logging, private internal files, temporary cache usage, and complete data cleanup.