Around 1:30 AM on May 23, 2020, a large number of Samsung phone users in China experienced severe issues including freezes, infinite reboots, and forced entries into Recovery Mode. Improper handling by users led to data loss, and the incident quickly became a trending topic on social media platforms like Zhihu, with service centers overwhelmed.
Social media responses ranged from frustration to anger, with some comparing the incident to the Note 7 battery disaster, “charging-gate,” or “green-screen-gate.” Some predicted Samsung would be forced out of the Chinese market. People lost job offers due to missed calls, others lost valuable data, and some even resorted to smashing their devices in frustration.
As an Android developer, I am not here to pile on Samsung. My goal is to uncover the root cause of this incident and see what we can learn from it. Since it’s a technical failure in the Android system, it’s essential to analyze it from a technical perspective.
Historical case note (reviewed 2026-10-14): This analysis uses screenshots and decompiled fragments retained from 2020. AOD version numbers identify old samples, not current update recommendations. Original APK hashes, complete firmware, and Samsung’s build records are unavailable here, so this review separates directly observed errors, version-comparison clues, and build-cause hypotheses.

The circulated shop photo also shows display units in recovery. It does not by itself prove hardware damage or complete data loss.

Conclusion
The retained log directly shows com.android.systemui’s main thread failing to resolve an integer field in R$string while inflating an AOD-related date view, producing NoSuchFieldError. It does not establish that strings.xml simply lacked the “leap” character, or identify a particular Maven dependency error in Samsung’s build.
AOD means Always On Display, showing information such as time while the screen is otherwise off. Availability depends on the model and settings, not a universal high-end-only rule. This stack runs through a SystemUI lock-screen plugin path, not an independently crashing AOD process.
The Hong Kong Observatory’s 2020 conversion table places 2020-05-23 on the first day of the leap fourth month. The old method accesses common_date_leap_month when isLeapMonth() is true, supporting a date-triggered explanation. The branch applies to leap months generally, not only a leap fourth month. The Chinese original’s common_data_leap_month spelling should be common_date_leap_month, matching the log and code.
Repeated SystemUI failures may trigger recovery policies. A recovery image alone does not establish the escalation sequence on every device, or prove that every device with a listed version must enter recovery. Firmware, plugin versions, locale/region configuration, and the executed path determine exposure; the user’s country is not itself the code’s condition.
Analysis
If you were just here for the drama, you can stop at the conclusion above. For Android developers who are curious, let’s keep going. The investigation isn’t complicated, but I think it is worth walking through.
External analysis can locate clues from logs and version samples. Without build configuration, runtime class-loading evidence, and regression results, it is not a complete vendor root-cause report. I’ll go through the steps and tools I used at the time, along with what the retained material can actually tell us.
1. Starting from the Symptoms
The recovery photo shows an Android 9 / PPR1-based build. Recovery entry, a SystemUI process restart, and a whole-device reboot are separate events to verify.

AOSP Rescue Party, introduced in Android 8.0, escalates recovery actions. In android-9.0.0_r1, persistent-app crashes are counted by UID with a threshold of five within a 30-second window. Escalation resets settings before the final level requests recovery with a user prompt to wipe data. It is not a rule that any repeatedly crashing system process automatically erases the phone. Enablement and OEM implementations vary; the pictured Erase app data/Safe mode flow cannot be equated item by item with AOSP.
The next photograph supplies the error itself. I guessed at the time that Samsung had added its own diagnostic screen. The photo alone can’t confirm that; if you know what it is, please let me know.

The readable fields identify com.android.systemui, PID 2388, java.lang.NoSuchFieldError, common_date_leap_month, type I (int), and declaring class com.samsung.android.uniform.R$string. The stack includes LocalDateView.getLunarCalendarInChina, updateLocalDate, onFinishInflate, and SystemUI lock-screen clock views. LocalDataView in the old explanation was a typo.
The investigation therefore asks why this branch accesses the field and why the loaded class cannot resolve it. FATAL EXCEPTION is the log heading; NoSuchFieldError is the specific error type.
2. Code Analysis
I used TTDeDroid. At this review, its latest published release is v1.8.0 (2024-06-12); bundled tools are not necessarily their upstreams’ current versions. JADX can also be used directly, but reconstructed Java is not a guarantee of complete or exact source recovery.
I found several AOD versions on APKMirror to compare. A reproducible comparison needs source, SHA-256, versionName/versionCode, signing identity, firmware build, and installed path. A named version may have variants, and a downloaded APK need not equal the device’s system package. The labels below belong to the original investigation; these are not newly unpacked or device-tested samples.
Normal Version (V5.2.05)
“Normal” is the original comparison label, not today’s latest version or an acceptance result across models. The shown branch is:
1 | String month = shouldShowLeapMonth(locale) ? context.getResources().getString(R.string.common_date_leap_month) + months[convertMonth] : months[convertMonth]; |
It accesses the field when shouldShowLeapMonth(locale) is true. The screenshot does not show that helper’s implementation, so its full conditions cannot be inferred from its name. The older method directly calls isLeapMonth(); the two should not silently be treated as identical. The retained samples do not establish how frequently the branch executes.

The next screenshot shows the field in com.samsung.android.app.aodservice.R, with value 2131689531. This is not the failing com.samsung.android.uniform.R$string class. A field in one namespace does not prove its presence in the other, and resource IDs should not be hardcoded across packages or versions.

The decoded resource image shows common_date_leap_month with text “闰.” A resource-table entry and a runtime class field are separate evidence.

Affected Version (V4.1.70)
I treated v4.1.70 as a suspect sample: the search window shows a reference but no definition.

That warrants inspecting the missing field, but does not prove that every DEX, resource table, and dynamically loaded class was examined. The method name matches the stack; decompiler line numbers do not establish exact build identity.

The original fragment follows. SECCalendarFeatures, the converter, and Time depend on the sample; this is not a standalone standard-Android example to compile:
1 | private String getLunarCalendarInChina(Context context) { |
Only the leap-month branch accesses the field. Array bounds checks cannot prevent field-resolution failure. Inspect DEX field references and the runtime ClassLoader to identify the actual R class, rather than relying solely on the Java view.
3. When did the bug appear?
The retained materials support this evidence table:
| Sample | Retained material | Supported reading |
|---|---|---|
| v3.3.18 | Search lists app.aodservice and uniform fields | Both definitions appear in that decompilation |
| v4.0.57 | Search shows only a reference; original notes date it 2018-10-24 | Suspected introduction point, without complete artifact verification |
| v4.1.70 | Reference and method screenshots | Consistent with the failing path, not a device reproduction |
| v4.2.24 / v4.2.44 | APK listing dated 2019-05-03 / 2019-06-24 | Listing dates, not independent proof of the fix or OTA coverage |
| v5.2.05 | New branch, R field, and resource screenshots | Later comparison sample, not today’s latest release |

The v3.3.18 search includes definitions in both namespaces:

The v4.0.57 search shows only the use:

From the samples I found at the time, I placed the fix at v4.2.44. A corresponding fix commit or complete binary comparison is unavailable here. The range must not include v4.2.44 as universally faulty while simultaneously naming it as fixed; listing dates also do not prove all models received an update. The Chinese caption’s 4.3.44 typo is corrected to the pictured 4.2.44.
The Chinese agricultural calendar is lunisolar, not purely lunar. Leap months are not a once-in-a-decade event. Test the general isLeapMonth branch across leap months, rather than waiting for another leap fourth month.
4. The Build Issue
The question that bothered me at this point was: if the field can’t be found, how did this compile in the first place?
NoSuchFieldError is a runtime linkage failure: a required field is incompatible with the actual loaded class definition. Separate three checks:
- Can compilation resolve the field in the required fully qualified R class?
- Does the final DEX/runtime-loaded class have the matching name and field type?
- Can the integer ID resolve an entry in the active Resources/resource table?
Failure at step 2 can occur before getString executes. Missing resources at step 3 usually involve Resources.NotFoundException. The following character entry therefore cannot, by itself, explain NoSuchFieldError.

Resources need not originate in the main module’s strings.xml: libraries and resource merging also supply R symbols. Java’s constant rules can resolve constant values into caller code during compilation; a decompiler’s R.string expression does not necessarily mean a named DEX field read remains. Modern builds also cannot assume all R fields are final: AGP 8.0 defaults to non-final, non-transitive R. This is modern context, not an explanation of Samsung’s 2018 build.
Mixed dependency versions, class/resource removal during packaging, host/plugin incompatibility, or a ClassLoader selecting another definition are candidates to inspect. The screenshots cannot choose among them or establish the original “second Maven case.” They identify the runtime failure and triggering branch, but not the internal build step responsible.
For a fresh investigation, preserve original packages, inspect Manifest/DEX with apkanalyzer, inspect resource tables with AAPT2, and correlate runtime class origins with the system/plugin combination. Regress the actual release artifacts rather than checking only that the IDE shows the field.
Modern update: Android 16 introduced the Crash Recovery module, including PackageWatchdog and RescueParty. Trace source locations and recovery policy for the actual OS version; the old article is not a current universal recovery contract.
Summary
The reasoning wasn’t especially complicated, but finding versions, decompiling them, and checking the code took some tedious work. The direct clue is a calendar branch triggering runtime field-resolution failure. The date and error type can be checked; the exact build mismatch, complete affected-version range, and each device’s recovery sequence require more original evidence.
Lessons Learned:
Feature Testing: Cover ordinary/leap months, month/year boundaries, time zones, locale, lock-screen view recreation, and plugin updates. Control time in tests instead of waiting for the real date.
Dependency Management: Check compile-time inputs, final DEX, resources, runtime class origins, and host/plugin combinations. Compilation alone does not establish release compatibility.
UI Stability: SystemUI and Launcher failures can affect essential interaction. Test plugin compatibility and isolate recoverable failures where appropriate; engineers must balance feature work with stability. They also sit between system development and app development, with dependencies on both sides. It isn’t easy. My wife had been doing this work for over six years when I wrote this—she deserved an extra chicken drumstick that evening…
Updates: Use official updates matching the model, region, and system, read release notes, and back up important data. Updates may fix defects or introduce regressions; do not assume system-app updates are risk-free or install arbitrary mismatched system APKs.
Curiosity and Humility: Curiosity helps me look past the symptoms and learn something new. Humility reminds me how little I know in an Android system this large.
This was a serious incident for Samsung, and a case worth learning from. Samsung was already struggling in China, and the S20 series had only just shown signs of recovery when this happened. Own the mistake and apologize. There’s no shame in that.
About Me && Blog
Below is my personal intro and related links. I look forward to exchanging ideas with fellow professionals. “When three walk together, one can always be my teacher!”
- Blogger Intro
- Blog Content Navigation: A guide for my blog content.
- Curated Excellent Blog Articles - Android Performance Optimization Must-Knows
- Android Performance Optimization Knowledge Planet: The paid community stopped operating in 2026 and no longer accepts new members; this link preserves its history.
One walks faster alone, but a group walks further together.
