NullPointerException: Unable to start receiver ru.solrudev.ackpine.impl.installer.receiver.PackageInstallerStatusReceiver
Recently this crash log appeared several times in sentry
RuntimeException
Unable to start receiver ru.solrudev.ackpine.impl.installer.receiver.PackageInstallerStatusReceiver: java.lang.NullPointerException
android.app.ActivityThread in handleReceiver at line 4315
android.app.ActivityThread in -$$Nest$mhandleReceiver
android.app.ActivityThread$H in handleMessage at line 2153
android.os.Handler in dispatchMessage at line 106
android.os.Looper in loopOnce at line 201
android.os.Looper in loop at line 288
android.app.ActivityThread in main at line 7872
java.lang.reflect.Method in invoke
com.android.internal.os.RuntimeInit$MethodAndArgsCaller in run at line 548
com.android.internal.os.ZygoteInit in main at line 936
Not sure if it's related somehow - prior to crash, I see several consecutive breadcrumbs about low memory on device
Additional Info: ackpine = "0.9.4" device: Nexus 5, Android 13
@nvkleban I think low memory is likely related to this crash. NPE (without message) can't be thrown in PackageInstallerStatusReceiver under normal conditions. Other than that nothing can be said about this log.
But just in case, do you configure preapproval for install sessions?
Edit: I missed that this log is from Android 13, which makes my last question irrelevant.
Is it possible that this part can produce null pointer? Session is nullable and there is a cast to a non nullable type
private fun handlePreapprovalResult(
session: CompletableSession<InstallFailure>?,
status: Int,
message: String?,
intent: Intent
) {
session as PreapprovalListener
when (status) {
PackageInstaller.STATUS_SUCCESS -> session.onPreapproved()
else -> session.complete(
Session.State.Failed(InstallFailure.fromIntent(intent, status, message))
)
}
}
@nvkleban Yeah, I was asking that question specifically because of this section. But, as I wrote, this is irrelevant for Android 13, because preapproval is only available on 14+. Furthermore, this cast can only be triggered when the sesssion is non-null under normal conditions, because it's the session who triggers execution of the receiver.
I’ll keep an eye on the incoming logs and follow up if new information becomes available.
I have a sudden spike of this kind of reports after our new release with partial rollout, since the last one we updated ackpine version 0.10.1 -> 0.11.0 Reports from various devices like Pixel 9 Pro, bunch of Samsungs from S, A series, Honor 50, Honor 90, Huawei - feels like everyone affected. I've used myself Pixel 9 Pro and seems like this "crash" is unnoticeable for me as a user.
@nvkleban Does this exception affect an installation outcome?
Which Android versions are affected?
Android 13, 14, 15 We actually managed to reproduce it on a QA device, and crash actually was visible for a user but installation was successful.
We rolled back version lib to 0.10.1 - so far seems no issues.
Stacktrace
java.lang.RuntimeException: Unable to start receiver ru.solrudev.ackpine.impl.installer.receiver.PackageInstallerStatusReceiver: java.lang.NullPointerException
at android.app.ActivityThread.handleReceiver(ActivityThread.java:4905)
at android.app.ActivityThread.-$$Nest$mhandleReceiver
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2498)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:230)
at android.os.Looper.loop(Looper.java:319)
at android.app.ActivityThread.main(ActivityThread.java:8919)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:578)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)
java.lang.NullPointerException: null
at ru.solrudev.ackpine.impl.installer.receiver.PackageInstallerStatusReceiver.onReceive(PackageInstallerStatusReceiver.kt:61)
at android.app.ActivityThread.handleReceiver(ActivityThread.java:4896)
at android.app.ActivityThread.-$$Nest$mhandleReceiver
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2498)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:230)
at android.os.Looper.loop(Looper.java:319)
at android.app.ActivityThread.main(ActivityThread.java:8919)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:578)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)
Seems like it affected ~15% of our userbase, but it was a small % rollout release
@nvkleban That's great that you've managed to reproduce it! Are there any specific steps to get this crash?
Not really just regular in-app update, happened on emulator and this time somehow it was noticeable, before it seems like crash was silent
@nvkleban What session parameters are used? Is this a self-update?
This is self-update
val apkUri = apk.run(Uri::fromFile)
val result = try {
val installerType = if (miuiUtil.isBuggedMiui()) InstallerType.INTENT_BASED else InstallerType.SESSION_BASED
Logger.d("Installer type: $installerType")
val params = InstallParameters.Builder(apkUri)
.setConfirmation(Confirmation.IMMEDIATE)
.setInstallerType(installerType)
.setName(apk.name)
.setRequireUserAction(true)
.build()
val session = packageInstaller.createSession(params)
coroutineScope {
val logJobs = logSession(session)
val sessionResult = session.await()
logJobs.forEach(Job::cancel)
sessionResult
}
}
I can't reproduce this on Android 15 emulator with Pixel 9 Pro virtual device.
Regarding the cause of the NPE, it's an intent extra. I don't understand why it would be null. All pending intents for the receiver have UPDATE_CURRENT flag, so extras shouldn't be removed. Nothing else besides Ackpine's sessions would use the receiver. Also the only meaningful change in the receiver between versions 0.10.1 and 0.11.0 possibly related to this NPE was adding one more extra to the intent, so a surge of crashes is curious.
That said, I've tried some changes. Please, test a snapshot 0.11.2-SNAPSHOT version. It's hosted on Central Portal maven-snapshots repository.
So far looks good. I’ve tested it multiple times on both the emulator and a personal device. Have you identified the possible cause?
@nvkleban Like I wrote, a Serializable extra from intent in the receiver was null in crash cases (as seen from your stack trace). It's a UUID, so now I put it to the extras bundle as two long values. If they're not in the extras, the UUID bits will be all zeros, but in this case nothing will happen, because session with this UUID would never be found as it's an invalid UUID v4. So it's not that I fixed the core issue, I just circumvented it. I could also just return from onReceive() if this UUID is null, but I thought maybe the fact that it's a Serializable object causes the extra to be absent sometimes for some unknown reason.
What I need to know now is whether all sessions always get their state updated correctly without issues, because if absent extra does affect it, then it's not a viable fix.
Hmm, I'll take a closer look a little bit later. I've also cancelling all old sessions before install maybe it somehow affects this, maybe some kind of race condition issue.
@nvkleban I don't think that could cause the problem. I'll appreciate testing on your side though.
I was checking the changes between 0.11.0 and 0.10.1 and noticed that some extra keys were changed, can this be a reason why id was null? For i.e. old version saved extra under one key and new version looks value with new key? It's just a guess I still don't have clear picture in mind how the whole installation process works.
-internal const val EXTRA_ACKPINE_SESSION_ID = "ACKPINE_SESSION_ID" +internal const val EXTRA_ACKPINE_SESSION_ID = "ru.solrudev.ackpine.extra.ACKPINE_SESSION_ID"
@nvkleban That's a good catch! Indeed, this change was the cause of NPE surge. I missed it because I was under impression that BroadcastReceiver doesn't receive intents after app update. And it doesn't, but as it turns out, only on some devices.
Version 0.11.2 containing the fix for NPE surge was released.
We have the next release planned in May, I'll close this issue after testing it on a variety of user devices.
Seems like widespread issue was resolved. I still see though original crash logs on Nexus 5X, it's probably some issue related to custom OS on this device. As far as I know 8.1 is the last Android version that was shipped to this device but in Sentry I see Android 13.
Updated stacktrace:
java.lang.IllegalArgumentException: PackageInstallerStatusReceiver: ackpineSessionId was null
at ru.solrudev.ackpine.impl.helpers.SessionIdIntents.getSessionId$ackpine_core_release(com.google.android.gms:play-services-mlkit-barcode-scanning@@18.3.1:38)
at ru.solrudev.ackpine.impl.installer.receiver.PackageInstallerStatusReceiver.onReceive(PackageInstallerStatusReceiver.kt:66)
at android.app.ActivityThread.handleReceiver(ActivityThread.java:4306)
@nvkleban If ackpineSessionId is null, it means something is fundamentally wrong with intent extras on the affected ROM. It can be null only if absolutely no extras containing the session's UUID were found, neither under new or old keys. I have no idea if it's possible to do something with it.
I'll close this issue as most likely this is a ROM issue.