[GH-ISSUE #7810] [Bug] inspect_android_package rewrites nested-class component names (Outer$Inner → Outer.Inner) in declared_name, name and the manifest text #4645

Closed
opened 2026-10-31 00:51:48 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @Rinkia on GitHub (Oct 28, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/7810

Before submitting

  • I searched the open and closed issues for a related report (declared_name, jadx manifest, inner class receiver, get_android_manifest). Related: #6866 (closed, wrong manifest selected), which is a different defect.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Android analysis

Observed behavior

A manifest component can name a nested class, for example WorkManager's androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy. inspect-android-package reports it as ConstraintProxy.BatteryChargingProxy in components.items[].declared_name, in name, and in manifest.text. docs/android-analysis.md says that "declared_name preserves the source attribute".

The dot spelling comes from the JADX get_android_manifest text, which writes Java-style inner class names. The binary AndroidManifest.xml string pool and the DEX type (L…/ConstraintProxy$BatteryChargingProxy;) both use $, and no dot form exists anywhere in the APK.

Example: base.apk (sha256 af6aa033…ff75) of the AirNav Radar 3.4.17 bundle (com.airnav.radarbox, version code 20221043), with androguard 4.1.3 AXMLPrinter as the reference:

androguard (binary manifest) inspect-android-package
Components 94 94
Names with $ 6 0; the same 6 are spelled with .
Examples androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy (and $BatteryNotLowProxy, $NetworkStateProxy, $StorageNotLowProxy), androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver, com.facebook.AuthenticationTokenManager$CurrentAuthenticationTokenChangedBroadcastReceiver ….ConstraintProxy.BatteryChargingProxy, ….ForceStopRunnable.BroadcastReceiver, com.facebook.AuthenticationTokenManager.CurrentAuthenticationTokenChangedBroadcastReceiver
All other component fields (exported, permission, foreground service type, intent actions) agree

These are library components (an older androidx WorkManager, the Facebook SDK), so any app that ships them shows the same thing. inspect-android-class accepts both spellings, so REA's own lookups still work. But the component name is what the platform and other tools use: adb shell am broadcast -n com.airnav.radarbox/androidx.work.impl.background.systemalarm.ConstraintProxy.BatteryChargingProxy, Class.forName, a Frida Java.use or a diff against aapt2 dump xmltree need $.

Reproduction

rea inspect-android-package base.apk --format json   # components.items[].declared_name, manifest.text

Reference:

from androguard.core.axml import AXMLPrinter
import zipfile
x = AXMLPrinter(zipfile.ZipFile("base.apk").read("AndroidManifest.xml")).get_xml_obj()
print([e.get("{http://schemas.android.com/apk/res/android}name") for e in x.find("application") if "$" in (e.get("{http://schemas.android.com/apk/res/android}name") or "")])

rea-agents@6.8.0 (bundled JADX engine jadx-headless-mcp 0.7.1), Linux x64 container. I didn't find a public open-source APK that still declares nested-class components (current WorkManager no longer does); any manifest with a <receiver android:name="pkg.Outer$Inner"> reproduces it.

Expected behavior

declared_name keeps the attribute exactly as stored in the binary manifest ($), as documented, and name is the loadable class name (also $). If the manifest text has to come from JADX, either read component names from the binary manifest, or say in a limitation that the text uses JADX's Java-style inner class spelling.

Impact and scope

Static Android inventory only. Component names for nested classes can't be used as-is for am/cmd intents, instrumentation or comparison with other tools, and the documented declared_name contract doesn't hold for them.

Originally created by @Rinkia on GitHub (Oct 28, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7810 ### Before submitting - [x] I searched the open and closed issues for a related report (declared_name, jadx manifest, inner class receiver, get_android_manifest). Related: #6866 (closed, wrong manifest selected), which is a different defect. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Android analysis ### Observed behavior A manifest component can name a nested class, for example WorkManager's `androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy`. `inspect-android-package` reports it as `ConstraintProxy.BatteryChargingProxy` in `components.items[].declared_name`, in `name`, and in `manifest.text`. `docs/android-analysis.md` says that "`declared_name` preserves the source attribute". The dot spelling comes from the JADX `get_android_manifest` text, which writes Java-style inner class names. The binary `AndroidManifest.xml` string pool and the DEX type (`L…/ConstraintProxy$BatteryChargingProxy;`) both use `$`, and no dot form exists anywhere in the APK. Example: `base.apk` (sha256 `af6aa033…ff75`) of the AirNav Radar 3.4.17 bundle (`com.airnav.radarbox`, version code 20221043), with `androguard` 4.1.3 `AXMLPrinter` as the reference: | | androguard (binary manifest) | `inspect-android-package` | |---|---|---| | Components | 94 | 94 | | Names with `$` | 6 | 0; the same 6 are spelled with `.` | | Examples | `androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy` (and `$BatteryNotLowProxy`, `$NetworkStateProxy`, `$StorageNotLowProxy`), `androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver`, `com.facebook.AuthenticationTokenManager$CurrentAuthenticationTokenChangedBroadcastReceiver` | `….ConstraintProxy.BatteryChargingProxy`, `….ForceStopRunnable.BroadcastReceiver`, `com.facebook.AuthenticationTokenManager.CurrentAuthenticationTokenChangedBroadcastReceiver` | | All other component fields (exported, permission, foreground service type, intent actions) | | agree | These are library components (an older androidx WorkManager, the Facebook SDK), so any app that ships them shows the same thing. `inspect-android-class` accepts both spellings, so REA's own lookups still work. But the component name is what the platform and other tools use: `adb shell am broadcast -n com.airnav.radarbox/androidx.work.impl.background.systemalarm.ConstraintProxy.BatteryChargingProxy`, `Class.forName`, a Frida `Java.use` or a diff against `aapt2 dump xmltree` need `$`. ### Reproduction ```sh rea inspect-android-package base.apk --format json # components.items[].declared_name, manifest.text ``` Reference: ```python from androguard.core.axml import AXMLPrinter import zipfile x = AXMLPrinter(zipfile.ZipFile("base.apk").read("AndroidManifest.xml")).get_xml_obj() print([e.get("{http://schemas.android.com/apk/res/android}name") for e in x.find("application") if "$" in (e.get("{http://schemas.android.com/apk/res/android}name") or "")]) ``` `rea-agents@6.8.0` (bundled JADX engine `jadx-headless-mcp` 0.7.1), Linux x64 container. I didn't find a public open-source APK that still declares nested-class components (current WorkManager no longer does); any manifest with a `<receiver android:name="pkg.Outer$Inner">` reproduces it. ### Expected behavior `declared_name` keeps the attribute exactly as stored in the binary manifest (`$`), as documented, and `name` is the loadable class name (also `$`). If the manifest text has to come from JADX, either read component names from the binary manifest, or say in a limitation that the text uses JADX's Java-style inner class spelling. ### Impact and scope Static Android inventory only. Component names for nested classes can't be used as-is for `am`/`cmd` intents, instrumentation or comparison with other tools, and the documented `declared_name` contract doesn't hold for them.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4645
No description provided.