OTA Update for a persistent android priv-app (Staged APK installation)
Using staged Package Manager installation to update system applications without modifying the system partition
Updating a regular Android application is straightforward:
Install new APK
↓
Stop old process
↓
Replace/update package
↓
Start new versionBut what happens when the application is a persistent privileged system app?
That’s where things get interesting.
A persistent app is expected to remain running as part of the system. Android can automatically restart it if necessary, and more importantly, the normal package-update flow has a problem:
You need to stop the application to update it, but a persistent application is specifically designed not to be stopped.
This is where staged installation becomes useful.
The problem with updating a persistent app
Consider a privileged system app:
<application
android:persistent="true"
... />It may be running continuously:
System boot
↓
Package Manager starts app
↓
App process running
↓
System continues using itNow suppose version 101 is available while version 100 is currently running.
A normal update needs to transition from:
MyApp v100 → MyApp v101But Package Manager cannot simply replace the package while the old process is actively using it.
For a normal application, Android can stop the application as part of the update.
For a persistent system application, however, stopping the process is not a reliable option. The system expects the process to remain available.
So we have a chicken-and-egg problem:
To update:
↓
Need the old app stopped
↓
But persistent app shouldn't be stopped
↓
How do we safely activate the new version?Enter staged installation
The solution is to separate installing the update from activating it.
Instead of:
Install → immediately activatewe do:
Stage update
↓
Commit
↓
Wait
↓
Reboot
↓
Activate new versionThis is exactly what a staged Package Manager session provides.
At the API level:
PackageInstaller.SessionParams params =
new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);params.setStaged();The important flag is:
params.setStaged();It tells Package Manager that the session should be applied as a staged install, with activation occurring across a reboot boundary.
Why does reboot solve the problem?
Because reboot naturally gives us something that is difficult to achieve while Android is running:
Every process is gone.
During the reboot:
REBOOT
│
▼
Persistent app stopped
│
▼
System shuts down
│
▼
Android boots
│
▼
Package Manager processes
staged installation
│
▼
New app version activeThere is no need to forcibly stop the persistent application while the system is running.
Get Shekhar Agarwal’s stories in your inbox
Join Medium for free to get updates from this writer.
The reboot itself provides the clean transition point.
That’s the key value of staged installation in this scenario.
A simple example
Suppose the device currently has:
MyPersistentApp v100and we want to deploy:
MyPersistentApp v101Instead of immediately replacing v100:
v100
│
├── running
│
└── update requested ❌we stage v101:
v100 v101
│ │
│ running │ staged
│ │
└────────────┬───────────┘
│
reboot
│
▼
v101Before reboot, the device continues running the old version.
After reboot, Package Manager activates the new version.
This gives us a clean boundary between the two versions.
The important Package Manager flag
If you’re experimenting with this from adb, the equivalent operation is:
adb shell pm install -r --staged MyApp.apkThe important part is:
--stagedYou can also enable rollback:
adb shell pm install -r \
--staged \
--enable-rollback \
MyApp.apkNow the update can be prepared before reboot and activated after reboot, with rollback support available if the staged update subsequently needs to be reverted.
Why not simply stop the persistent app?
You might ask:
“Why not just kill the process, perform the update, and let Android restart it?”
Because that defeats much of the reason the application is persistent in the first place.
A persistent system application may be part of the system’s expected runtime state. Forcing it down introduces a period where:
System expects service
↓
Process killed
↓
Service unavailable
↓
Potential inconsistent system stateStaged installation avoids creating that runtime gap.
Instead:
Old version
│
│ continues running
│
▼
REBOOT
│
│ clean transition
▼
New versionThe bigger idea
Staged installation isn’t merely an alternative way of installing an APK.
Its real value is that it provides a controlled activation point.
For persistent system applications, that activation point is particularly useful because the application cannot simply be treated like an ordinary application that can be stopped, replaced and restarted whenever an update arrives.
The model becomes:
Running system
│
│
Stage update
│
▼
Update prepared
│
│
reboot
│
▼
Old process disappears
│
▼
Package Manager activates
new package
│
▼
Persistent app starts
with new versionAnd that’s the key reason staged installation matters for persistent privileged apps:
It moves the difficult part of the update — stopping and replacing a continuously running system process — from runtime into the reboot boundary, where the process is naturally stopped anyway.
For a persistent priv-app, that small architectural shift can make the difference between a fragile live update and a clean, deterministic one.






