Removes 5 AndroidLeakFixes enum constants that are no longer needed
with minimum SDK 26+:
- MEDIA_SESSION_LEGACY_HELPER (API 21 only)
- CONNECTIVITY_MANAGER (API ≤23 only)
- ACTIVITY_MANAGER (Samsung API 22 only)
- IMM_FOCUSED_VIEW (API ≤23 only)
- SPELL_CHECKER (API 23 only)
These fixes were already no-ops with early returns after the min SDK
upgrade. Removing them completely eliminates ~200 lines of dead code
and documentation while maintaining all functional leak fixes.
The remaining 10 AndroidLeakFixes enum constants continue to provide
useful leak fixes for Android 26+.
Co-Authored-By: Claude <noreply@anthropic.com>
With minSdkVersion now set to API 26 (Android 8.0), many of the Android leak fixes in the plumber module are no longer necessary as they targeted older API levels. This commit resolves lint warnings by adding early returns to obsolete fixes and removing unnecessary API annotations.
Changes made:
- AndroidLeakFixes.kt: Added early returns to 6 leak fixes that only applied to API levels below 26:
• MEDIA_SESSION_LEGACY_HELPER (was for API 21)
• FLUSH_HANDLER_THREADS (was for API 14-25)
• LEAK_CANARY_THREAD_EDGE_CASE (was for API 21-25)
• ACTIVITY_THREAD_EDGE_CASE (was for API 16-25)
• CONNECTIVITY_MANAGER (was for API 21-25)
- FixedWindowCallback.java: Removed obsolete @RequiresApi(23) annotation from onSearchRequested method
- Cleaned up unused Build.VERSION imports after removing version checks
These changes eliminate 11 ObsoleteSdkInt lint errors while maintaining API compatibility. The affected leak fixes are no longer needed since Android 8.0+ doesn't have these issues.
Co-Authored-By: Claude <noreply@anthropic.com>
Fix two correctness bugs:
- INSTANCE_DUMP: stackTraceSerialNumber is always u4 (4 bytes), not
identifier-sized. This was silently corrupting output for heap dumps
with 8-byte identifiers.
- PRIMITIVE_ARRAY_DUMP: CopyingSource.transferUnsignedByte() already
copies the type byte to the sink — the extra sink.writeByte(type) call
was writing it twice, corrupting all primitive array records.
Fix a third bug in CopyingSource.transferUtf8: it decoded bytes as
UTF-8 into a Java String then re-encoded, which produces different byte
counts for Modified UTF-8 sequences (used by HotSpot HPROF). The fix
reads the raw bytes and copies them unchanged, only decoding to String
for the return value.
Other changes:
- Add StreamingSinkProvider (mirrors StreamingSourceProvider) so the
sink lifecycle is managed inside the function via .use {}
- Replace BufferedSink parameter with StreamingSinkProvider in the
streaming overload
- Refactor parsing into a private stripPrimitiveArrays(CopyingSource)
method
- Add CopyingSource.indexOf delegate
- Also updates all primitive wrapper instance values to 0 (in addition
to primitive arrays), as an additional PII safety measure
- Add deleteInputHprofFile parameter to delete the input file eagerly
after the source is opened, using the Unix trick
- Clean up test: replace raw-byte helpers with HprofWriter-based
fixture, add JVM heap dump tests for string replacement and wrapper
value zeroing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit migrates the entire LeakCanary project from minimum SDK API 21
to API 26, enabling modern Android features and further simplifying the codebase.
API 26 corresponds to Android 8.0 (Oreo), released in 2017, providing
over 7 years of Android compatibility while enabling notification channels
and modern development practices.
Changes included:
## Build Configuration
- Update androidMinSdk from "21" to "26" in version catalog
- Update hardcoded minSdk value in leakcanary-app from 21 to 26
- Update comment to reflect modern Android features requirement
## GitHub Actions / CI
- Remove API 21 and API 23 from test matrix
- Streamline CI to test only API 26+ (26, 31, 33, 34)
- Improve CI efficiency with fewer test combinations
## Code Modernization
- Simplify notification builder to always use notification channels (API 26+ requirement)
- Remove obsolete PendingIntent FLAG_IMMUTABLE version checks (API 23+ always available)
- Simplify instant app detection (API 26+ always supports this)
- Remove obsolete SDK version checks for dynamic shortcuts (N_MR1 no longer relevant)
- Modernize Context.getColor() usage (API 23+ always available)
- Simplify process utilities in UiAutomator (API 23+ features always available)
- Remove obsolete @TargetApi annotations for API levels below 26
## Resources & UI
- Move adaptive icons from mipmap-anydpi-v26 to mipmap-anydpi (no qualifier needed)
- Move app launcher icons from drawable-v24 to drawable (API 26+ always supports adaptive icons)
- Remove obsolete resource version qualifiers
## Code Cleanup
- Remove unused import statements after version check removals
- Simplify conditional logic where version checks became always true/false
- Remove dead code branches for pre-API 26 compatibility
## Benefits
- Access to notification channels (API 26 requirement) without compatibility code
- Simplified codebase with removed legacy compatibility workarounds
- Modern UI patterns with adaptive icons as standard
- Faster CI builds with fewer test matrix combinations
## Compatibility
All modules now consistently require Android 8.0 (API 26, 2017) or higher.
This affects minSdk but does not change the public API surface.
Library consumers benefit from simplified, more reliable notification handling.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit migrates the entire LeakCanary project from minimum SDK API 14
to API 21, enabling modern Android features and simplifying the codebase.
API 21 corresponds to Android 5.0 (Lollipop), released in 2014, providing
over a decade of Android compatibility while enabling Material Design and
modern development practices.
Changes included:
## Build Configuration
- Update androidMinSdk from "14" to "21" in version catalog
- Standardize all modules to use libs.versions.androidMinSdk reference
- Update hardcoded minSdk values in sample and library modules to use TOML
## GitHub Actions / CI
- Remove API 16 and API 19 from test matrix
- Streamline CI to test only API 21+ (21, 23, 26, 31, 33, 34)
- Improve CI efficiency with fewer test combinations
## Test Infrastructure
- Upgrade androidx.test:orchestrator from 1.4.2 to 1.5.0
- Modernize test assertions to use direct assertThat(Throwable) calls
- Remove API 16 workaround comments and code
## Code Modernization
- Remove obsolete SDK version checks (9 lint issues resolved)
- Simplify file provider methods for API 21+ only
- Update notification builder usage for modern APIs
- Remove legacy view tree observer workarounds
- Modernize layout and rendering code
## Resources & UI
- Migrate drawable resources from selectors to Material ripple effects
- Update themes from Holo to Material Design (API 21+ requirement)
- Merge and remove obsolete -v21 resource folders
- Modernize button and touch feedback using ripples
## Benefits
- Access to Material Design components and APIs
- Simplified codebase with removed legacy compatibility code
- Enhanced test infrastructure with latest orchestrator
- Improved user experience with modern UI patterns
- Faster CI builds with fewer test matrix combinations
## Compatibility
All modules now consistently require Android 5.0 (API 21, 2014) or higher.
This affects minSdk but does not change the public API surface.
Co-Authored-By: Claude <noreply@anthropic.com>
Move the explanation to the preceding comment line instead of appending
it to the annotation itself, per project style conventions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* origin/main:
Pin gradle/actions/wrapper-validation to commit SHA to satisfy code scanning
Drop API 28 and 29 from instrumentation test matrix
Fix LeakActivityTest on API 33+ by disabling notifications during test
Fix INSTALL_FAILED_DEPRECATED_SDK_VERSION on API 28+ for leakcanary-android tests
Upgrade wrapper-validation-action to gradle/actions/wrapper-validation@v4
Add recent Android API levels to instrumentation test matrix
Fix pkill self-match: use qemu-syste[m] pattern
Update changelog entry wording for ToastEventListener fix
Fix emulator process hanging 20+ min after CI tests finish
Add changelog entry for ToastEventListener race condition fix
Fix ToastEventListener race condition retaining finished Toast
Fixes "Insecure GitHub Actions: Third-Party Action Not Pinned to Commit SHA"
flagged by GitHub Advanced Security on PR #2803. Pins to the commit SHA
corresponding to v4 while keeping the tag as a comment for readability.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Based on timing analysis across two full CI runs:
- API 28 averaged 652s (~11 min), slowest after API 29
- API 29 averaged 672s (~11 min), consistently the slowest new addition
API 31, 33, and 34 provide good modern Android coverage at reasonable
cost (568–622s avg). APIs 28 and 29 can be reconsidered later if
Android 9/10 coverage becomes a specific need.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When targetSdk >= 33 and notifications are not enabled (fresh emulator),
Notifications.canShowNotification launches RequestPermissionActivity with
FLAG_ACTIVITY_NEW_TASK to request POST_NOTIFICATIONS permission. This
pushes LeakActivity to PAUSED, causing Espresso's onView() to throw
NoActivityResumedException.
The test is validating the heap import flow, not notifications. Setting
showNotifications = false prevents the permission activity from launching
so LeakActivity stays in the RESUMED state for the Espresso assertions.
The tryAndRestoreConfig wrapper already restores the original config.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Android 9 (API 28) and above refuse to install APKs that target
SDK version < 23. The leakcanary-android module was missing an explicit
targetSdk in its defaultConfig, causing the test APK to default to
targetSdkVersion = minSdkVersion = 14.
leakcanary-android-core already had this fix with an explanatory comment.
Apply the same to leakcanary-android so it can run on API 28-34 emulators.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The old gradle/wrapper-validation-action@v1 was silently failing on all
PR runs (works on push-to-main but not on PR merge commits). The modern
replacement is gradle/actions/wrapper-validation which is actively
maintained by Gradle.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add API levels 28, 29, 31, 33, and 34 (Android 9-14) to the CI
emulator matrix. API 29 was previously marked flaky but is worth
retrying with current tooling. All use x86_64 + google_apis + stable
channel, consistent with the existing modern emulator configuration.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On API 26 google_apis, the emulator process does not exit after
receiving adb emu kill. The android-emulator-runner post-step
polls until the process dies, which never happens before the
30-minute job timeout triggers, wasting ~23 minutes of CI time.
Force-kill the qemu-system process at the end of the test script,
before the action's post-step cleanup runs. When the post-step
then checks whether the process has exited, it is already gone
and the cleanup step exits immediately.
showToastBlocking() blocks the heap dump thread (via CountDownLatch)
until onAnimationEnd fires, but only up to a 5-second timeout. If the
timeout fires before the animation completes, the heap dump proceeds and
HeapDump posts a cleanup runnable to the main thread. That cleanup runs
while toastCurrentlyShown is still null (animation not done yet), so it
is a no-op. Then onAnimationEnd fires and sets toastCurrentlyShown = toast
permanently — no subsequent event clears it, causing a memory leak.
The fix: set toastCurrentlyShown = toast immediately after toast.show(),
before starting the animation. The cleanup runnable then always sees a
non-null reference and cancels + nulls it correctly, regardless of
whether onAnimationEnd has fired yet.
The partial fix in ade42f792 (adding toastCurrentlyShown = null to the
cleanup) addressed the common case but not this timeout-driven race.
Fixes#2301Fixes#2325
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
What These Changes Mean:
Removed Methods: getDefaultImpl() and setDefaultImpl()
These were part of an older AIDL pattern for handling default implementations when the service isn't available. This was a fallback mechanism that's been deprecated in favor of better error handling.
Added: DESCRIPTOR Field
This is the standard AIDL interface descriptor string - it's always been there internally, but now it's explicitly exposed in the public API.
Added: LeakUiApp$_Parcel Class
This is a newer AIDL tooling artifact - modern AIDL compilers generate additional helper classes for parcelization.
Impact Analysis: ✅ Likely SAFE - No Breaking Changes for Clients
Why this is probably fine:
1. Internal APIs: These are in org.leakcanary.internal.* package - the internal namespace signals these aren't meant for public consumption
2. AIDL Evolution: The Android AIDL compiler has been evolving to generate more modern, efficient code. The removed methods were legacy patterns.
3. Backwards Compatible:
- The core sendHeapAnalysis() method remains unchanged
- The DESCRIPTOR field addition doesn't break existing clients
- The new _Parcel class is just tooling infrastructure
4. LeakCanary Context: This appears to be inter-process communication between LeakCanary components, not a public API for app developers
⚠️ Potential Concerns:
1. If any external code was using the removed methods (unlikely since they're internal APIs)
2. Binary compatibility for any code that was directly instantiating or reflection-accessing these classes
What Could Have Caused This:
1. Java 17 + newer Android Gradle Plugin - may use a newer AIDL compiler
2. Updated compileOptions - could affect how AIDL generates code
3. Build environment changes - newer toolchain versions
Recommendation:
This is likely safe because:
- It's internal LeakCanary APIs
- The core functionality (sendHeapAnalysis) is preserved
- This follows AIDL tooling evolution patterns
However, you should consider:
- Testing: Ensure LeakCanary still works properly between processes
- Release notes: Document that internal AIDL APIs changed (if you publish these changes)
- Monitoring: Watch for any reports of inter-process communication issues
The changes appear to be tooling evolution rather than functional changes, but it's worth testing the actual LeakCanary functionality to be sure.
Updates Android instrumentation test dependencies and configuration to work with compileSdk 35 and API level 36:
- Update Gradle from 8.2 to 8.10.2
- Move from KAPT to KSP
- Update AGP to 8.2.2 and Gradle to 8.2
- Update Hilt to consistently be 2.53
- Update AndroidX Test dependencies to modern versions compatible with API 36
- Fix test namespace configuration to match actual package structure
- Move TuPeuxPasTest to correct package structure for proper test discovery
- Fixes build warnings about packagingOptions
- Add missing Java sourceCompatibility declarations
- Appease lint by suppressing the failures, we could remove the checks but they where written for a reason so keeping them
This ensures UI tests are properly discovered and executed by Gradle with correct reporting, while maintaining compatibility with newer Android APIs.
Co-Authored-By: Claude <noreply@anthropic.com>
When a cached AVD snapshot has LeakCanary packages installed with different
signing certificates than the current build, Android refuses to reinstall them.
Fix by explicitly uninstalling all known LeakCanary packages before running
connectedCheck, so the fresh build APKs always install cleanly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes NullPointerException when analyzing heap dumps from newer Compose versions
where CompositionImpl uses "state" int field instead of "disposed" boolean field.
The inspector now gracefully handles both field structures:
- Old Compose: disposed boolean field (backward compatibility)
- New Compose: state int field with values RUNNING=0, DISPOSED=3, etc.
- Edge case: neither field exists (graceful fallback)
Before: NullPointerException at AndroidObjectInspectors$COMPOSITION_IMPL$inspect line 792
After: Analysis completes successfully with proper state detection
Fixes: https://github.com/square/leakcanary/issues/2781
Co-Authored-By: Claude <noreply@anthropic.com>