Sitemap
Press enter or click to view image in full size

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 version

But 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 it

Now suppose version 101 is available while version 100 is currently running.

A normal update needs to transition from:

MyApp v100  →  MyApp v101

But 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 activate

we do:

Stage update
↓
Commit
↓
Wait
↓
Reboot
↓
Activate new version

This 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 active

There 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 v100

and we want to deploy:

MyPersistentApp v101

Instead of immediately replacing v100:

v100
│
├── running
│
└── update requested ❌

we stage v101:

v100                     v101
│ │
│ running │ staged
│ │
└────────────┬───────────┘
│
reboot
│
▼
v101

Before 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.apk

The important part is:

--staged

You can also enable rollback:

adb shell pm install -r \
--staged \
--enable-rollback \
MyApp.apk

Now 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 state

Staged installation avoids creating that runtime gap.

Instead:

Old version
│
│ continues running
│
▼
REBOOT
│
│ clean transition
▼
New version

The 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 version

And 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.

--

--