Skip to content

fix: resolve SQLite unique constraint crash in Work Profiles#2504

Open
domedav wants to merge 1 commit into
celzero:mainfrom
domedav:workprofile-database-constraints-fix
Open

fix: resolve SQLite unique constraint crash in Work Profiles#2504
domedav wants to merge 1 commit into
celzero:mainfrom
domedav:workprofile-database-constraints-fix

Conversation

@domedav

@domedav domedav commented Jan 12, 2026

Copy link
Copy Markdown

Fixed a critical ANR / Crash cycle occurring on Android 15 when the app is running within a Work Profile.

The crash was caused by a SQLiteConstraintException in the ProxyApplicationMapping table. During profile refreshes or package updates, Android 15 sometimes reassigns UIDs in a way that creates collisions with existing database entries. The previous tombstoneApp implementation performed a blind update, which triggered a unique constraint violation and corrupted the database state.

… 15)

Fixed a critical ANR / Crash cycle occurring on Android 15 when the app is running within a Work Profile.

The crash was caused by a SQLiteConstraintException in the ProxyApplicationMapping table.
During profile refreshes or package updates, Android 15 sometimes reassigns UIDs in a way that creates collisions with existing database entries.
The previous tombstoneApp implementation performed a blind update, which triggered a unique constraint violation and corrupted the database state.
@domedav domedav changed the title fix: resolve SQLite unique constraint crash in Work Profiles (Android… fix: resolve SQLite unique constraint crash in Work Profiles Jan 12, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a crash/ANR loop caused by SQLiteConstraintException when updating ProxyApplicationMapping UIDs (notably in Android 15 Work Profiles), by replacing a single SQL update with a transactional sequence intended to avoid unique/primary-key collisions.

Changes:

  • Converts tombstoneApp(oldUid, newUid) from a single UPDATE query into a @Transaction method that deletes conflicting rows before updating UIDs.
  • Adds DAO helpers to delete by UID and to update UID using UPDATE OR IGNORE as an extra safety measure.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +105 to +123
@Transaction
fun tombstoneApp(oldUid: Int, newUid: Int) {
// Only apply the logic bellow, if the uid is from a work profile
if (newUid >= 1_000_000) {
// If a record with the 'newUid' already exists, delete it first
// This prevents an application crash: database constraint
deleteMappingByUid(newUid)
}

// Now that the slot is empty, move the 'oldUid' records to 'newUid'
updateUidByOldUid(oldUid, newUid)
}

@Query("delete from ProxyApplicationMapping where uid = :uid")
fun deleteMappingByUid(uid: Int)

// 'ignore' provides an extra layer of safety
@Query("update or ignore ProxyApplicationMapping set uid = :newUid where uid = :oldUid")
fun updateUidByOldUid(oldUid: Int, newUid: Int)
Comment on lines +107 to +108
// Only apply the logic bellow, if the uid is from a work profile
if (newUid >= 1_000_000) {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants