The short answerAndroid starts the app, not your code. It reads the manifest, starts a process running as the app's own Linux user, and opens the screen the manifest names. No other app can open that user's private folder: apps share through Android.
The film
Film coming soon
Android opens the screen the manifest names
A Java program starts in main(), a Node script at its first line; an Android app does not start itself. It carries a manifest: the list of its activities, the screens Android can open, and of the permissions it may use.
Tap the icon of an app that is not running: Android opens the activity marked as the launcher, the screen the icon opens. It calls its onCreate(): only then does your screen's code run.
A permission the manifest does not declare is never granted: no dialog even appears.
Your messages app's manifest
AndroidManifest.xml in the built app
<manifest package="com.tmimiexplains.messages "><uses-permission android:name="android.permission.CAMERA" /><application><activity android:name="com.tmimiexplains.messages.MainActivity" android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>
uses-permission
What the app may use
Declared here, so the app may ask. Not declared: never granted, and no dialog.
activity · LAUNCHER
The screen the icon opens
Android calls its onCreate(). The app never starts itself.
Android starts a process, as the app's own user
At install, Android gives the app its own Linux user, like an account, with a private folder. Both are tied to its package name, the app's unique name.
When you tap the icon, Android starts a process if the app has none: the app running in memory, numbered by its PID. Expo, Flutter and Ionic apps get their own user too, once built. An installed web app (PWA) runs inside Chrome; its data lives in Chrome's folder.
One app, four names
| Your messages app | Given by | Lasts | |
|---|---|---|---|
| Package name | com.tmimiexplains. | you, in the project | as long as you keep it |
| Linux user | u0_a231 | Android, at install | until uninstall; updates keep it |
| Private folder | /data/data/ | Android, at install | until uninstall |
| Process (PID) | 4127 → 5310 | Android, at each start | until the process ends |
Coming back reuses a process still in memory: Why does your message sometimes vanish when you come back to an app?
Each start gets a new PID, always as the same user.
Other apps stay out; sharing goes through Android
Your banking app runs as another user, so Linux, the core under Android, keeps it out of your app's folder.
To share, apps go through Android. They use a link that opens another app, a shared folder such as Download, or the share sheet, Android's list of apps to send to. Android checks each exchange.
Go deeper The ways apps share, as you will meet them in a project 1 min
| The way, and its real name | What you see in the project |
|---|---|
| Share button: an intent, through the Android Sharesheet | an <intent-filter> in the receiving app |
| Links: a deep link; an App Link, which the website vouches for | an <intent-filter> with the address |
| Shared folders: shared storage, through the media store or the system pickers | none for your own files or with a picker; a media permission to read other apps' media |
| Data an app offers: a content provider; a FileProvider lends one file to one app, for a while | a <provider> |
| An app's service: a bound service | an exported <service> |
| Announcements: a broadcast | a <receiver>, or one registered in code |
| Copy and paste: the clipboard, held by Android | nothing to declare |
Each one starts with a call to Android, which checks who calls and whether the target accepts.
Two apps could also talk over the network, like any two programs: Android checks only their network permissions, not the conversation.
Who opens what
- Your messages app runs as
u0_a231, and opens its own private folder. - The banking app runs as
u0_a198. It asks for your folder, and Linux refuses:u0_a198≠u0_a231. - To share a message, your app hands it to Android, which gives Chrome a copy.
- A photo saved to
Pictures/goes through Android, into a shared folder.
Back to your messages app
Android read its manifest, started process 4127 as user u0_a231, and only then called its code. Your banking app runs as u0_a198, so Linux keeps your messages closed to it.
The rule
Your app gets only the permissions its manifest declares, and opens only its own private folder. Everything else goes through Android.
Recap
- The app does not start itself. The tap opens the manifest's launcher screen.
- A permission not declared is never granted, and no dialog appears.
- Each app runs as its own user. Linux keeps its folder closed; apps share through Android.
- Each start, a new PID.
What changes when the app starts again When the app starts again Package name same Linux user same Private folder same Process (PID) new
With knowledge · in class · 12 minutes
Read the manifest before the code
- Tell
- Check
- Test
-
Tell the assistant
Paste it before you describe your app. App already made? Add: “Apply these rules to my project.”
lowercase, no hyphen Type it: Copy waits for it.This app runs on Android.This web app (PWA) runs in Chrome on Android.Package name: com.<yourname>.<app>, never the template's placeholder.Permissions: only those its features use; tell me which feature needs each one.Exported: only the launcher activity.Photos and files: the system photo picker; to hand out a file, the share sheet or the system file picker, never its path.In its replyPaste the Permissions: line again and ask the assistant why.
-
Check what it wrote
The files decide, not the reply.
Look atWhereTick what you see
-
Package name, the app's unique name
applicationIdin Gradle Scripts → build.gradle.kts (Module :app)android.packageinapp.jsonapplicationIdinandroid/app/ build.gradle.kts appIdincapacitor.config.tsandapplicationIdinandroid/: change bothapp/ build.gradle Ask the assistant to change it now: later, it would be another app. Then uninstall the old app: its icon stays on the phone.
-
Exported, open to other apps
app → manifests → AndroidManifest.xml
android/app/ src/ main/ AndroidManifest.xml It exists once
npx expo run:androidhas run.Paste the Exported: line again and ask the assistant why.
-
Permissions, in your code
Search
getUserMedia,geolocation,requestPermission(Ctrl+Shift+F)Paste the Permissions: line again and ask the assistant why.
-
-
Test on your phone
Run the latest version: the phone decides.
Needs adb:
adb deviceslists your phone. Not yet? Before class, just below.-
Permissions, in Settings
Settings → Apps → your app → PermissionsChrome → Settings → Site settings → All sites → your site
Settings lists only what a user can switch off; adb lists them all.In Expo Go you see Expo Go's: test your own build, made by
npx expo run:android.The site asks Chrome for each one.Paste the Permissions: line again and ask the assistant why.
-
Permissions, with adb
adb shell "dumpsys package com.<yourname>.<app> | grep -i permission"Read the lines under
requested permissions:Ask the assistant to remove it (
READ_MEDIA_IMAGES,MANAGE_EXTERNAL_STORAGE): your private folder and the photo picker need none.Block it (READ_MEDIA_IMAGES,MANAGE_EXTERNAL_STORAGE) withandroid.blockedPermissionsinapp.json: your folder and the photo picker need none.
-
-
Debrief
What did you find that you never asked for? Who put it there?
One answer
Usually the template's package name, and permission lines that the template or a library adds (AndroidX adds one ending in
DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION). Neither you nor the assistant typed them: that is why you read the files and the phone, not only its reply.Usually nothing. If there is a permission, a library's code asked Chrome for it: ask the assistant which one, and why.
Go deeper Every build, side by side 1 min
| Build | Package name (the template's) | Permissions and manifest |
|---|---|---|
| Java, Compose | applicationId in app/ (com.example.…) | app/ |
| Expo | android.package in app.json (missing, or com.anonymous.…) | android.permissions in app.json, and android.blockedPermissions for what a library adds. The manifest exists once npx expo run:android has made android/. Test your own build, not Expo Go. |
| Flutter | applicationId in android/ (com.example.…) | android/ |
| Ionic | appId in capacitor.config.ts and applicationId in android/, copied once: change both (io.ionic.starter) | android/ |
| PWA | none to write: Chrome installs it from your web manifest | none: the site asks Chrome for each permission; its data lives in Chrome's folder |
Try it on your phone · 4 minutes
See the app's user, a new process, a closed folder
These steps need adb, installed once: see Before class, just below.
Before class Install adb 5 min
adb, the Android Debug Bridge, is a command-line tool that sends your laptop's commands to your phone. Turn on Developer options, as in Who draws the pixels of a mobile app?, then USB debugging, and accept the prompt on the phone. Install the Android SDK Platform-Tools; Android Studio already has them, in %LOCALAPPDATA%\ on Windows. adb devices must list your phone. On Windows, inside that folder, type .\adb instead of adb, or add the folder to your PATH.
Copy the commands as they are: the quotes make the filter run on the phone, so the same lines work from Windows, macOS and Linux.
Empty list, or “unauthorized”? Unlock the phone and accept the prompt, try another cable, or install your phone maker's USB driver. No cable? The same lines work on Android Studio's emulator.
-
Who runs Chrome. Open Chrome, press Home, then list its process.
adb shell "ps -A -o USER,PID,NAME | grep com.android.chrome$"You should seeu0_a160 4116 com.android.chromeThe first column is Chrome's Linux user, whose number differs from phone to phone; the second is its PID. The final
$keeps only Chrome's own line. Nothing printed? Open Chrome, then run it again.This proves Chrome runs as its own Linux user.
-
A new start, the same user. Stop Chrome with the command form of Force stop. It prints nothing.
adb shell am force-stop com.android.chromeOpen Chrome again, press Home, and list its process as in step 1.
adb shell "ps -A -o USER,PID,NAME | grep com.android.chrome$"You should seeu0_a160 4624 com.android.chromeThis proves Each start gets a new process, always as that user.
-
Its private folder. Ask the phone's shell to list it.
adb shell ls /data/data/com.android.chromeYou should seels: /data/data/com.android.chrome: Permission deniedThis proves The phone's shell, where adb runs your commands, is another Linux user,
shell, so Linux refuses it.
Real outputs, captured on 27 October 2026 on an Android 16 emulator (Pixel 7 profile, API 36), the same in PowerShell and cmd. Your user numbers and PIDs will differ.
Quick check
The user taps your app's icon. What tells Android which screen to open?
See the answer
Android starts the app from its manifest. Your source folders, and any main() in them, do nothing until it does.
The assistant's code requests the camera permission, but the manifest has no CAMERA line. What does the user see?
See the answer
What is not declared cannot be granted, and the manifest is fixed when the app is built: add the line, rebuild, and the dialog appears.
Your app must hand one attachment, kept in its private folder, to an email app. What works?
See the answer
The email app runs as another user, so your path stays closed to it, and All files access opens shared storage, never another app's private folder. Through the share sheet, a FileProvider lends that one file to that one app, for a while.
You test your Expo app in Expo Go, then open Settings → Apps to check its permissions. Whose do you find?
See the answer
Your own package and permissions exist only once your app itself is built: a development build (eas build or npx expo run:android) or a store build.
Sources
- Android Developers: Application fundamentals, a Linux user per app; the Application reference, its code runs before any activity; AOSP: Application sandbox; Configure the app module, a changed application ID makes another app.
- Permissions on Android and Request runtime permissions, declared in the manifest first.
- Data and file storage overview, the private folder and shared storage; Access media files from shared storage; Photo picker; Sharing files, FileProvider.
- Intents and intent filters; About deep links; Android 12 behavior changes,
android:exported. - Expo: app config,
android.package,android.permissionsandandroid.blockedPermissions; Capacitor: Android configuration; web.dev: WebAPKs on Android, an installed PWA runs in Chrome.