Added
- GIF Creator slide interval control: Added time between slides slider (0.5s–5.0s in 0.5s increments) replacing the previous FPS control on both Mobile and TV interfaces.
- Feature to generate animated GIF files from selected images with custom frame rate, resizing options, and drag-and-drop frame reordering on Mobile and Android TV.
- Support for generating GIFs directly from local storage and remote network/cloud shares.
gifencoder(v0.10.1) library integration for fast on-device GIF encoding.- Added
.m3uand.m3u8playlist file support to built-in Text Editor with syntax highlighting for M3U directives. - Added built-in EPUB reader. Tapping any
.epubfile now opens it directly in UFM without requiring an external app. The viewer supports EPUB 2 and EPUB 3 formats, renders chapters in a WebView with previous/next chapter navigation and a table of contents, respects the app-wide font size preference, and switches to a dark background automatically when the device is in dark mode. Available on both mobile and Android TV. No third-party library was added — parsing uses the standard Android platform APIs.
Changed
- Improved Batch Rename preview layout to display both original and modified filenames stacked vertically with a compact before → after layout, while automatically collapsing redundant original names when no change occurs.
Fixed
- Fixed list scroll position jumping to the top when a file or folder operation (rename, delete, paste, refresh) triggers a directory reload. The
RecyclerViewnow preserves the exact scroll position across same-folder refreshes in all browser surfaces — local file browser (Activity and Twin Window Fragment), network/online browsers (SMB, FTP, SFTP, NFS, WebDAV, GDrive, OneDrive, Dropbox, etc. — Activity and Twin Window Fragment), on both mobile and Android TV. TV D-pad back-navigation focus restoration is unaffected. - Fixed folder picker mode handling in GIF Creator custom folder selection across StorageBrowserActivity, FileBrowserActivity, and NetworkBrowserActivity to suppress feature tiles and correctly display the "Use This Folder" FAB button.
- Fixed instant sync triggering for SMB and SFTP servers when moving or copying files into local source folders.
- Fixed opening internal storage folders showing stale 0-byte ghost files when indexing is active. When files were moved or deleted on disk by Advanced Sync or background processes, Room database index records persisted until a manual refresh. The file list now validates physical file existence (
File.exists()) when loading indexed paths, preventing non-existent files from rendering as 0-byte ghosts inFileBrowserFragment,FileBrowserActivity, andSearchActivity. In addition,FileBrowserActivitynow triggers background folder index synchronization upon folder navigation, andAdvancedSyncWorkernotifiesIndexingRepositoryandMediaScannerNotifierimmediately upon local file deletions during move and sync operations. - Fixed SMB connection drops and infinite loading spinner caused by session invalidation on back gestures, hyper-aggressive session staleness timeout, and socket teardown hangs when returning from idle sessions.
- Fixed the "Show Hidden Files" setting in the Sort & Filter sheet bleeding between custom (folder-specific) and global scopes. When a folder had a custom sort with "Show Hidden Files" enabled, opening the sort sheet in any other folder — which uses the global scope — incorrectly showed the custom folder's hidden-files state instead of the global setting, and saving either scope would overwrite the other's value. The sheet now initialises "Show Hidden Files" from the folder's stored custom state when a folder override exists, and only updates the global
HiddenFilesManagerflag when the user saves in the Global scope. Fixed inFileBrowserActivity,FileBrowserFragment,NetworkBrowserActivity, andNetworkBrowserFragment. - Fixed a crash (
java.lang.UnsatisfiedLinkError) when compressing or extracting Zstandard archives (.zst,.tar.zst,.tzst) on Android — reported from a vivo I2219, SDK 36, app 1.8.1-FOSS. The app depended on the plaincom.github.luben:zstd-jniJAR, which bundles only desktop native libraries; on Android zstd-jni resolves its resource path fromos.name("linux") and looked for/linux/aarch64/libzstd-jni-1.5.7-12.so, which is not shipped for the APK, then fell back toSystem.loadLibrary("zstd-jni-1.5.7-12"), which failed because the native library was never packaged — so every.zst/.tar.zstoperation crashed on a background worker thread. The app now depends on the zstd-jni AAR artifact (@aar), which packages the Android native libraries into the APK'slib/<abi>/(arm64-v8a, armeabi-v7a, x86_64, x86) so the native library loads successfully. As a safety net,ArchiveManagernow catchesLinkageError(e.g. a device where the native codec still cannot load) and fails the compression/extraction operation instead of crashing. - Fixed new text files created on SMB shares incorrectly displaying a phantom size of 2.15 GB in the file list; empty files now correctly show
0 Buntil content is saved. - Fixed file copy and move operations to a NAS (SMB, FTP, SFTP, NFS, WebDAV, and all other network share types) failing when the screen turns off mid-transfer.
TransferServicenow acquires aPARTIAL_WAKE_LOCKand aWifiLockdirectly inonStartCommandand releases them inonDestroy, ensuring the CPU and Wi-Fi radio remain active for the full duration of the transfer regardless of Activity lifecycle. Also changed the service return value fromSTART_NOT_STICKYtoSTART_REDELIVER_INTENTso Android restarts the service if it is killed under memory pressure while a transfer is in progress. - Fixed AVIF images not displaying and thumbnails not rendering on devices running Android 7–10 (API 24–30) and on some devices running Android 12+ (API 31+). The previous
avif-androidfallback library required API 26 minimum, leaving devices on Android 7–8 (API 24–25, e.g. certain MiBox 4 firmware) with no AVIF decoder at all. The AVIF brand detection (isAvif) only peeked 64 bytes of theftypbox, causing some valid AVIF files whose brand appears late in the compatible-brands list to be silently rejected on all devices (including Vivo iQOO Z9x on API 36). Additionally,AvifDecoder.Factorywas only registered in theImageViewerActivityandSlideShowActivitylocal CoilImageLoaderinstances — the global Coil singleton used byFileAdapter(file browser thumbnails) had no AVIF decoder at all. Replacedorg.aomedia.avif.android:avifwithio.github.awxkee:avif-coder(API 24+), which uses a bundledlibavif + libdav1dnative decoder to cover API 24–30; API 31+ still uses the systemImageDecoder. Expanded theftypbox peek from 64 to 256 bytes so AVIF files with late-appearing brands are correctly detected. RegisteredAvifDecoder.Factory(along withAnimatedPngDecoder,GifDecoder/AnimatedImageDecoder, andSvgDecoder) in the global Coil singleton viaUfmApplication.newImageLoader()(implementingSingletonImageLoader.Factory), ensuring AVIF thumbnails and viewing work in every screen — not just the dedicated viewer. Network AVIF thumbnail decoding inNetworkThumbnailCacheManageralso updated to useHeifCoder.decodeSampled()for API 24–30, which decodes directly at target thumbnail dimensions rather than full resolution, preventing OOM on low-RAM devices. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread inside the frame-skip logging that a device-vendor ROM injects into the frame-rendering pipeline — e.g.
Choreographer.doFrame→com.transsion.hubcore.view.TranChoreographerImpl.skippedFrames(TECNO's ROM-installed Choreographer hook) →android.util.Slog.e→android.util.Log.println_native(top frame), reached from the vsync frame callbackChoreographer$FrameDisplayEventReceiver.runfreshly dispatched to the main looper byHandler.handleCallback— reported from a TECNO TECNO KJ5, SDK 33, app 1.7.8-FOSS. The sampled frame is the vendor hook emitting the platform's standard "Skipped N frames" warning:Log.println_native/Slog.eis a fast log-buffer write (µs-scale) that cannot by itself occupy the main thread for 5 s, and a thread genuinely parked inside a >5 s block cannot be processing a freshly dispatched vsync frame callback at sample time, so the >5 s block occurred in a PREVIOUS main-looper message and this sample is the post-stall first frame after recovery. The stack has zeroza.kilowatch.ultimatefilemanagerframes, and the vendor hook's class names (com.transsion.*, injected by the TECNO ROM, not part of this app) are not platform-prefixed so the pure-framework filter did not match — this is the pure frame-skip logging variant of the already-filtered TranssionTranChoreographerImpl.skippedFramesbinder service-lookup shape. TheAnrWatchdogThreadnow treats a main-thread stack whose top frame is a Log/Slog emission (Log.println_native/printlnorSlog.e), containing acom.transsion.hubcore.view.TranChoreographerImpl.skippedFramesframe, aChoreographer.doFrameframe and aHandler.handleCallbackframe, with noza.kilowatch.ultimatefilemanagerframes, as a false positive and resets its heartbeat instead of writing a report. Genuine freezes keep the main thread inside app business logic — an app frame on the stack, or a top frame that is not a Log/Slog emission under the vendor'sskippedFrames(e.g. a lock, file I/O, or binder frame) — and are still reported. - Fixed an ANR (App Freeze) when the main thread is blocked inside a blocking
java.io.File.delete()call — the nativeLinux.removesyscall — executed directly from aDispatchers.Maincoroutine resumed viaHandler.handleCallback— reported from a KTC JVC 2K TV, SDK 34, app 1.8.0-GOOGLE. Several UI-triggered delete flows ran their per-fileFile.delete()/deleteRecursively()loops on the main thread: on slow or busy storage (a singleremove()syscall on a low-end TV) the delete can exceed the 5 s ANR watchdog threshold and freeze the UI. The blocking deletes now run onDispatchers.IO, returning to the main thread only for the list/adapter/toast updates:StorageAnalyzerActivity.deleteDuplicates(),FolderDuplicateFinderActivity.deleteDuplicates(),FolderLargeFilesFinderActivity.executeDeletion()(launched on IO, UI updates back on Main),SearchActivity.confirmDelete()(the blocking delete moved into a background coroutine), andFileBrowserActivity's transfer-cancel cleanup of the incomplete destination file (moved off the cancel-button click listener onto IO). Deletion behaviour, protected-path handling, and index syncing are unchanged — only the thread the delete runs on moved. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread while the framework dispatches the Activity-resumed lifecycle event to the Application's registered
ActivityLifecycleCallbacksduring a normal Activity resume — e.g.Application.dispatchActivityResumed→<obfuscated callback>.onActivityResumed(UFM's Application-level lifecycle callback registered inUfmApplicationto apply the saved locale / font size / AMOLED background, top frame, caught at the callback's entry), underActivity.dispatchActivityResumed→Activity.onResume→<activity>.onResume→Instrumentation.callActivityOnResume→Activity.performResume→ActivityThread.performResumeActivity— reported from a TCL BeyondTV, SDK 30, app 1.8.0-GOOGLE. The sampled frame is a normal, fast Activity-resume lifecycle dispatch: the callback body is bounded (cachedLocaleHelper/FontSizeHelperprefs reads, config comparisons, a view lookup, and a conditionalactivity.recreate()), and the sample caughtonActivityResumedat its entry with no deeper frame into the body (noSharedPreferencesImpl,findViewById, orrecreateframe), so it cannot by itself occupy the main thread for 5 s; the >5 s block is device-side slowness / CPU starvation on the low-end TV (the report's ownDlnaFetchThread,DlnaSsdpListener,DefaultDispatcher-worker-*,NanoHttpd Main Listenerand HTTP server threads are all RUNNABLE) or a post-stall sample of the backlog the main looper drains after a genuine stall. TheAnrWatchdogThreadnow treats a main-thread stack whose top frame isonActivityResumedon a non-platform class, withApplication.dispatchActivityResumedandActivity.dispatchActivityResumedframes, as a false positive and resets its heartbeat instead of writing a report. Genuine freezes keep the main thread inside blocking work — the top frame is notonActivityResumed(it is a deeper frame such as a lock, file I/O, binder call, or the callback's own body executing blocking work, e.g. an uncachedgetSharedPreferencesread surfacing as aSharedPreferencesImplframe) — and are still reported. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread while the framework dispatches the Activity-resumed lifecycle event through an Activity's own
onPostResumehook during a normal Activity resume — top frameandroid.app.Activity.onPostResume(the platform's empty post-resume hook), reached from a non-platform (R8-obfuscated app/library) frame — the app Activity'sonPostResumeoverride calling super — underActivity.performResume→ActivityThread.performResumeActivity→handleResumeActivity→servertransaction.ResumeActivityItem.execute— reported from a CADENA CADENA PRO W2 CSB-243, SDK 30, app 1.8.1-FOSS. The currently executing frame is the framework's emptyonPostResumehook, which cannot by itself occupy the main thread for 5 s; the sample caught the app Activity'sonPostResumeoverride inside itssuper()call, before any of the app's own post-super work could run, so the >5 s block is device-side slowness / CPU starvation on a low-end device (the report's ownufm-startup-iothread is RUNNABLE doingMediaStoreclass-init,ufm-pairing-initis WAITING on a keystore lookup, and the watchdog's ownufm-anr-watchdogsampling thread is RUNNABLE) or a post-stall sample of the backlog the main looper drains after a genuine stall. TheAnrWatchdogThreadnow treats a main-thread stack whose top frame isandroid.app.Activity.onPostResume, with a non-platform frame directly below it (the app Activity's override calling super) and anActivity.performResumeframe on the stack, as a false positive and resets its heartbeat instead of writing a report. Genuine freezes keep the main thread inside blocking work — the top frame is notandroid.app.Activity.onPostResume(it is a deeper frame such as a lock, file I/O, binder call, or the Activity's own post-super work executing aftersuper.onPostResume()returned) — and are still reported. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread inside the main Handler's message-enqueue bookkeeping while a freshly dispatched main-looper Runnable schedules a delayed message — top frame
java.lang.ThreadLocal.get(an O(1) ThreadLocalMap lookup) underandroid.os.ThreadLocalWorkSource.getUidunderandroid.os.Handler.enqueueMessage, reached fromHandler.postDelayed/sendMessageDelayed, whose caller is a non-platformrun()sitting directly onandroid.os.Handler.handleCallback— reported from a Samsung SM-F966U, SDK 36, app 1.8.0-GOOGLE. The sampled Runnable was just entered and itsrun()is callingpostDelayedas its first action — the enqueue bookkeeping is µs-scale and therun()demonstrably hasHandler.handleCallbackdirectly below it, so the main looper is processing messages at sample time, which a thread parked inside a >5 s block cannot do; the >5 s block therefore occurred in a PREVIOUS main-looper message and this sample is the post-stall backlog the looper drains after recovery (the same family as therun()-top andStringBuilder-top post-stall filters, one frame further into the Runnable's entry). This is not the watchdog's own heartbeat ticker re-post (whichtickerJustRanalready handles): the ticker updateslastTickTimestampbefore callingpostDelayed, so a sample inside its re-post would carry a fresh timestamp and be suppressed — this report's timestamp was stale, so the Runnable is a different post-stall backlog entry. TheAnrWatchdogThreadnow treats a main-thread stack whose top frame isjava.lang.ThreadLocal.getunderandroid.os.ThreadLocalWorkSource.getUidunderandroid.os.Handler.enqueueMessage, with aHandler.postDelayed/sendMessageDelayedframe whose direct caller is a non-platformrun()sitting directly onHandler.handleCallback, as a false positive and resets its heartbeat instead of writing a report. Genuine freezes keep the main thread inside blocking work — the top frame is not the enqueue bookkeeping (it is a lock, file I/O, or binder frame) — and are still reported. - Fixed an ANR (App Freeze) when the main thread is blocked inside a
java.io.File.exists()/File.length()call — the nativeLinux.access/statsyscalls — in the slideshow while paging through images on a TV D-pad press — reported from a KONKA VU 4K Google TV, SDK 34, app 1.8.1-GOOGLE.SlideShowActivity.updatePageState()runs synchronously insideViewPager2.setCurrentItem()(invoked fromonKeyDown→navigateNext/navigatePrev), and for every local image it calledFile(path).exists()+File(path).length()on the main thread; on slow TV storage (USB/NFS mounts) a single stat exceeded the 5 s watchdog threshold, freezing the slideshow on each D-pad navigation. The per-image filesystem stat now runs onDispatchers.IOand only the resulting size string is applied back on the main thread, guarded so a stale stat from a previous page can't overwrite the currently displayed page's info text. Navigation, playback, zoom/pan, and page-change behaviour are unchanged — only the thread the stat runs on moved. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread inside a device-vendor ROM's Looper-observer hook at the start of a message dispatch — top frame
android.os.MessageQueue.nativeWakeunderMessageQueue.enqueueMessage→Handler.enqueueMessage→Handler.sendMessageAtTime→Handler.sendMessageDelayed→Handler.post, whose caller istcl.resource.LooperMonitor.getKernelInfoinvoked bytcl.resource.LooperMonitor.onDispatchStart, called directly byandroid.os.Looper.loop— reported from a TCL BeyondTV, SDK 30, app 1.8.1-GOOGLE. TCL's ROM injectsLooperMonitorinto the platform Looper soonDispatchStartruns at the start of every message dispatch; the sample caught the hook posting a delayed message back onto the main looper, which is µs-scale bookkeeping (nativeWake/enqueueMessage/post) that cannot by itself occupy the main thread for 5 s, and a thread parked inside a >5 s block cannot be at the entry of a message dispatch — so the >5 s block is device-side slowness / CPU starvation on a low-end TV or a post-stall sample of the backlog the looper drains after a genuine stall. The stack has zeroza.kilowatch.ultimatefilemanagerframes, and the vendor hook's class name (tcl.resource.LooperMonitor, injected by the TCL ROM, not part of this app) is not platform-prefixed so the pure-framework filter did not match. TheAnrWatchdogThreadnow treats a main-thread stack whose top frame isMessageQueue.nativeWakeunder the Handler message-enqueue chain, with a vendorLooperMonitor.onDispatchStart/getKernelInfoframe invoked fromandroid.os.Looper.loop, and no app frames, as a false positive and resets its heartbeat instead of writing a report. Genuine freezes keep the main thread inside blocking work — a lock, file I/O, or binder frame, or app business logic on the stack — and are still reported. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread inside the framework's native line-break computation while a RecyclerView lays out its rows during a normal frame — top frame
android.graphics.text.LineBreaker.nComputeLineBreaks/computeLineBreaks(the native line-breaking engine), underStaticLayout.generate(viaStaticLayout.<init>fromStaticLayout$Builder.build) →TextView.makeSingleLayout→TextView.makeNewLayout→TextView.onMeasure, underView.measure, underandroidx.recyclerview.widget.LinearLayoutManager/RecyclerView.onLayout, reached from a frame-draw traversal (Choreographer.doFrame→ViewRootImpl.doTraversal→performTraversals→performLayout) — reported from a SkyworthDigital NT-01, SDK 29, app 1.8.1-GOOGLE. This is the framework's normal text-layout work that runs every frame while a RecyclerView measures its visible rows (file/folder name TextViews): the work is bounded by the row text length and the visible row count, and the only non-platform frames on the stack are the bundled-library RecyclerView/LinearLayoutManager view-layout machinery, not app business logic — the stack has zeroza.kilowatch.ultimatefilemanagerframes. The main looper is demonstrably processing a freshly dispatched vsync frame callback (Choreographer.doFrame/Handler.handleCallback) at sample time, which a thread parked inside a >5 s block cannot do; the >5 s block is device-side slowness / CPU starvation on a low-end TV (the report's ownDlnaSsdpListener,eventLoopGroupProxy-*,NanoHttpd Main Listenerand HTTP-server threads are all RUNNABLE) or a post-stall sample of the backlog the main looper drains after a genuine stall. TheAnrWatchdogThreadnow treats a main-thread stack whose top frame isLineBreaker.nComputeLineBreaks/computeLineBreaks, with aStaticLayout.generate/StaticLayout$Builder.buildframe, aTextView.makeNewLayout/makeSingleLayoutframe, aTextView.onMeasureframe, aView.measureframe, anandroidx.recyclerview.widget.*frame, and a frame-draw dispatch (Choreographer.doFrame/ViewRootImpl.performLayout/performTraversals), and noza.kilowatch.ultimatefilemanagerframes, as a false positive and resets its heartbeat instead of writing a report. Genuine freezes keep the main thread inside app business logic — an app frame on the stack, or a text measurement not reached from a RecyclerView layout within a fresh frame-draw traversal (e.g. the Text Viewer's wrap-content EditText, which is capped app-side and has a ScrollView/LinearLayout path, not a RecyclerView frame) — and are still reported. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread inside the framework's native text measurement while an EditText processes a character committed by the IME, one frame further into the same
MeasuredText$Builder.build()— top frameandroid.graphics.text.MeasuredText$Builder.nFreeBuilder(the native free of the previous MeasuredText builder thatbuild()runs atMeasuredText.java:464before measuring the new paragraph), underMeasuredParagraph.buildForStaticLayout→StaticLayout.generate→DynamicLayout.reflow/DynamicLayout$ChangeWatcher.reflow, reached from the IME text-input path (BaseInputConnection.commitText/replaceText→ the editable'sreplace→SpannableStringBuilder.replace→sendTextChanged→ the TextView's watcher chain) — reported from a TECNO TECNO KJ5, SDK 33, app 1.7.8-FOSS, the same device and session that produced the already-filteredMeasuredText$Builder.nBuildMeasuredText/build,nDrawTextRun,SpannableStringBuilder.removeSpanandView.invalidatereports. This is the identical IME reflow chain as the previously-filterednBuildMeasuredText/build-top shape (filter 18), just sampled one frame further:nFreeBuilderis a bounded native resource free andMeasuredText$Builder.build()is the same framework text-measurement routine the filter already covers, so it cannot by itself occupy the main thread for 5 s; the >5 s block is device-side slowness / CPU starvation on a very low-end device or a post-stall sample. TheAnrWatchdogThreadIME-text-measurement filter now also treats a main-thread stack whose top frame isMeasuredText$Builder.nFreeBuilder— in addition to the existingnBuildMeasuredText/build— with aDynamicLayout.reflow/DynamicLayout$ChangeWatcher.reflowframe and aBaseInputConnection.replaceTextframe, as a false positive and resets its heartbeat instead of writing a report. Genuine freezes that keep the main thread inside app business logic — the top frame is not the native measurement, or the reflow is not reached from an IME text edit (noBaseInputConnection.replaceTextframe, e.g. the app callssetText/appenddirectly) — are still reported. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread inside the main Handler's message-enqueue bookkeeping at the entry of a freshly dispatched main-looper Runnable's first
postDelayedcall — top frameandroid.os.Message.obtain(the Message-pool allocationHandler.getPostMessageperforms to wrap the Runnable) underandroid.os.Handler.getPostMessageunderHandler.postDelayed/sendMessageDelayed, whose caller is a non-platformrun()(so1.run) sitting directly onandroid.os.Handler.handleCallback— reported from a Google Pixel 6a, SDK 37, app 1.8.1-GOOGLE. This is the same post-stall sampling artifact as the already-filteredThreadLocal.get-top enqueue shape (filter 31, from the Samsung SM-F966U report), just sampled one frame earlier at the very entry of the samepostDelayedcall:Message.obtain/getPostMessageare µs-scale enqueue bookkeeping, and therun()demonstrably hasHandler.handleCallbackdirectly below it, so the main looper is processing a fresh message at sample time, which a thread parked inside a >5 s block cannot do — the >5 s block occurred in a PREVIOUS main-looper message and this sample is post-stall backlog the looper drains after recovery. This is NOT the watchdog's own heartbeat ticker re-post (whichtickerJustRanalready handles): the ticker updateslastTickTimestampbefore callingpostDelayed, so a sample inside its re-post would carry a fresh timestamp and be suppressed; this report's timestamp was stale, soso1.runis a different post-stall backlog entry. TheAnrWatchdogThreadfilter 31 (isPostDelayedFromFreshRunStall) now also treats a main-thread stack whose top frames areMessage.obtain→Handler.getPostMessage→Handler.postDelayed/sendMessageDelayed— in addition to the existingThreadLocal.get→ThreadLocalWorkSource.getUid→Handler.enqueueMessageshape — with aHandler.postDelayed/sendMessageDelayedframe whose direct caller is a non-platformrun()sitting directly onHandler.handleCallback, as a false positive and resets its heartbeat instead of writing a report. Genuine freezes keep the main thread inside blocking work — the top frame is NOT the enqueue bookkeeping (it is a lock, file I/O, or binder frame) — and are still reported. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread inside the main Handler's message-enqueue bookkeeping at the very first instruction of a freshly dispatched main-looper Runnable's first
postDelayedcall — top frameandroid.os.Handler.postDelayed/sendMessageDelayeditself (the sample caught the Runnable's firstpostDelayedbefore it descended intogetPostMessage/Message.obtain), directly under a non-platformrun()(jf0.run) sitting onandroid.os.Handler.handleCallback— reported from a Google Pixel 6a, SDK 37, app 1.7.7-GOOGLE. This is the same post-stall sampling artifact as the already-filteredMessage.obtain-top (filter 31a) andThreadLocal.get-top (filter 31b) enqueue shapes, just sampled one frame earlier still, at the entry of the samepostDelayedcall:Handler.postDelayed/sendMessageDelayedis µs-scale enqueue bookkeeping, and therun()demonstrably hasHandler.handleCallbackdirectly below it, so the main looper is processing a fresh message at sample time, which a thread parked inside a >5 s block cannot do — the >5 s block occurred in a PREVIOUS main-looper message and this sample is post-stall backlog the looper drains after recovery. This is NOT the watchdog's own heartbeat ticker re-post (whichtickerJustRanalready handles): the ticker updateslastTickTimestampbefore callingpostDelayed, so a sample inside its re-post would carry a fresh timestamp and be suppressed; this report's timestamp was stale, sojf0.runis a different post-stall backlog entry. TheAnrWatchdogThreadfilter 31 (isPostDelayedFromFreshRunStall) now also treats a main-thread stack whose top frame isHandler.postDelayed/sendMessageDelayed— in addition to the existingMessage.obtain→Handler.getPostMessage→Handler.postDelayed/sendMessageDelayedandThreadLocal.get→ThreadLocalWorkSource.getUid→Handler.enqueueMessageshapes — with aHandler.postDelayed/sendMessageDelayedframe whose direct caller is a non-platformrun()sitting directly onHandler.handleCallback, as a false positive and resets its heartbeat instead of writing a report. Genuine freezes keep the main thread inside blocking work — the top frame is NOT the enqueue bookkeeping (it is a lock, file I/O, or binder frame) — and are still reported. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread inside a bundled-library view's
<init>while the framework cold-starts an Activity and inflates its content view — e.g. top frameandroidx.constraintlayout.widget.ConstraintLayout.<init>, reached viaLayoutInflater.createView/createViewFromTag/inflate→ the obfuscated AndroidXsetContentViewchain →LanguageWelcomeActivity.onCreate→Activity.performCreate→Instrumentation.callActivityOnCreate→ActivityThread.performLaunchActivity— reported from a SmartTV, SDK 31, app 1.8.1-AMAZON. A view constructor that the frameworkLayoutInflaterruns while inflating the Activity's layout is bounded, one-time cold-start work (class loading, attribute decoding, field init) that cannot by itself occupy the main thread for 5 s; the only app frame is the Activity's ownonCreatelifecycle callback the framework invoked, so the >5 s block is the one-time framework-driven cold-start cost (class loading, resource decode, layout inflation) on a low-end device, which the app cannot act on. TheAnrWatchdogThreadnow treats a main-thread stack whose top frame is a<init>on a non-platform, non-app (bundled-library) class, with aLayoutInflaterframe, asetContentViewframe, and a framework Activity cold-start-launch frame (Activity.performCreate/Instrumentation.callActivityOnCreate/ActivityThread.performLaunchActivity), where every app frame is an Activity class, as a false positive and resets its heartbeat instead of writing a report. Genuine freezes keep the main thread inside app business logic — a top frame that is not a bundled-library<init>under that chain (e.g. a lock, file I/O, or binder frame), an app frame that is not an Activity class (e.g. adapter bind code or a custom view constructor, whose class starts with the app package), or inflation not reached from an Activity cold-start launch — and are still reported. - Fixed a crash (
java.lang.RuntimeException:Unable to start service ... FileServerService ... AppWidgetManager.getAppWidgetIds(ComponentName) on a null object reference) when starting the FTP/SFTP file server on devices with no home-screen widget host (e.g. Android TV / Amazon Fire TV launchers) — reported from a SmartTV, SDK 31, app 1.8.1-AMAZON.FtpSftpWidgetProvider.updateAllWidgets()calledAppWidgetManager.getInstance(context)and immediately dereferenced the result;getInstance()returnsnullon devices without an AppWidget host, so theNullPointerExceptionpropagated out ofFileServerService.onStartCommand→showForegroundNotification()(which runs on the main thread and callsupdateAllWidgets()on every server start/stop and notification refresh), aborting the service start. The widget-refresh path is best-effort — when there is no widget host there is nothing to update — soupdateAllWidgets()now returns early whenAppWidgetManager.getInstance()isnull.SmartSortWidgetProvider.updateWidget()received the same null-guard for the identical latent crash. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread inside a synchronous binder call to the system server's ServiceManager while the framework fetches a system service for the app — e.g.
LanguageWelcomeActivity.onCreate→DeviceUtils.isTvDevice→getSystemService(UI_MODE_SERVICE)→ContextImpl.getSystemService→SystemServiceRegistry.getSystemService→android.app.UiModeManager.<init>→ServiceManager.getServiceOrThrow→ServiceManager.getService/ServiceManagerProxy.getService→IServiceManager$Stub$Proxy.checkService→BinderProxy.transact→transactNative(top frame) — reported from a SEI Robotics Nokia Streaming Box 8010, SDK 34, app 1.8.1-GOOGLE. The app merely invoked the one-line framework APIgetSystemService()to detect the Android-TV form factor (viaUiModeManager.currentModeType) and pick the mobile/TV layout; the >5 s block is the system server's response latency to the ServiceManager service-lookup transaction, which the app cannot act on. The top frame is the binder transact into the system server — the app is inside the round-trip, not executing business logic — the same class as the existingstartActivity(IActivityTaskManager$Stub$Proxy) andunbindServicebinder filters, but for thegetSystemServiceservice-fetch path (whose binder call goes toIServiceManager$Stub$Proxy.checkService/getServiceunderServiceManager/ServiceManagerProxy). TheAnrWatchdogThreadnow treats a main-thread stack whose top frame isBinderProxy.transact/transactNative, containing a ServiceManager service-lookup frame, reached through the framework's owngetSystemServiceservice-fetch path (SystemServiceRegistryorContextImpl.getSystemService), as a false positive and resets its heartbeat instead of writing a report. Genuine freezes that run app business logic have an app frame as the current frame (the top frame is notBinderProxy.transact/transactNative) and are still reported. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread inside the native thread-creation syscall while a thread pool creates a worker thread from a main-looper Runnable — e.g.
Handler.handleCallback→v1.run(a non-platform, R8-obfuscated runnable) →ThreadPoolExecutor.execute→ThreadPoolExecutor.addWorker→Thread.start→Thread.nativeCreate(top frame, the nativepthread_create) — reported from a Sony BRAVIA 4K AE2, SDK 34, app 1.8.0-GOOGLE. The app (or a bundled library) merely invoked the fire-and-forgetThreadPoolExecutor.execute()API to submit a task; the pool's core size was reached with no idle worker, so it created a new worker thread, and the >5 s block is inside the OS's native thread creation —Thread.nativeCreate(pthread_create) stalling under device memory pressure / CPU starvation on a low-end TV (the report's ownpool-2-thread-1,DlnaSsdpListener,DlnaFetchThreadandDefaultDispatcher-worker-2are all RUNNABLE, busy with the HTTP streaming server and SSDP/DLNA discovery, starving the main thread), which the app cannot act on. There is no lock, file/network I/O, or binder frame anywhere on the stack — the currently executing frame is the kernel thread-creation syscall, not app business logic, and the stack has zeroza.kilowatch.ultimatefilemanagerframes. TheAnrWatchdogThreadnow treats a main-thread stack whose top frame isThread.nativeCreate/Thread.start, withThreadPoolExecutor.addWorkerandThreadPoolExecutor.executeframes, the execute invoked from a non-platformrun()dispatched by the main Handler, and no app frames, as a device-side thread-creation stall and resets its heartbeat instead of writing a report. Genuine freezes keep the main thread inside blocking work — an app frame on the stack, or a top frame that is not the native thread-creation syscall under aThreadPoolExecutor.execute(e.g. app business logic creating threads in a loop, or a lock/file/binder block) — and are still reported. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread one frame INTO a freshly dispatched main-looper Runnable — top frame
o45.d(a non-platform, R8-obfuscated method the Runnable'srun()called as its first action), underba.run(the Runnable'srun()), sitting directly onandroid.os.Handler.handleCallback— reported from a Google Pixel 6a, SDK 37, app 1.8.1-GOOGLE. The sampled Runnable was just entered — itsrun()demonstrably hasHandler.handleCallbackdirectly below it and only calledo45.d()before the sample — so the main looper is processing messages at sample time, which a thread parked inside a >5 s block cannot do; the >5 s block therefore occurred in a PREVIOUS main-looper message and this sample is the post-stall backlog the looper drains after recovery (the same family as the bare-run()filter 20 andStringBuilderfilter 10 post-stall artifacts, one frame further into the Runnable's entry). The stack has no lock, file/network I/O, or binder frame anywhere (the current frame is a freshly-entered CPU-bound call, not a blocking primitive), and the report's own background threads (DefaultDispatcher-worker-2RUNNABLE constructing anSMBClientvia reflection inNetworkBrowserActivity,DlnaFetchThread/DlnaSsdpListenerRUNNABLE,NanoHttpd Main ListenerRUNNABLE) are busy — consistent with CPU contention / a post-stall sample. TheAnrWatchdogThreadnow treats a main-thread stack whose top frame is a non-platform method whose direct caller is a non-platformrun()sitting directly onandroid.os.Handler.handleCallback(with noHandler.postDelayedframe and no framework blocking primitive) as a false positive and resets its heartbeat instead of writing a report. Genuine freezes keep the main thread inside blocking work — a top frame that is a lock/file-I/O/binder frame, or a call chain more than one method deep above therun()— and are still reported. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread inside an R8-obfuscated bind/layout helper method while a RecyclerView lays out its rows during a normal frame — e.g. top frame
yb.funderyb.aunderyb.j(three frames in the same obfuscated class — the adapter's onBindViewHolder / ViewHolder bind chain), underandroidx.recyclerview.widget.RecyclerView.Z/s/r/onLayout, reached from a frame-draw traversal (Choreographer.doFrame→ViewRootImpl.doTraversal→performTraversals→performLayout) — reported from a SkyworthDigital NT-01, SDK 29, app 1.8.1-GOOGLE, the same device and session family that produced the already-filteredLineBreaker.nComputeLineBreaks-top RecyclerView-layout report (filter 33), sampled at the bind phase instead of the measure phase. This is the framework-driven per-row layout/bind work that runs every frame while the file list lays out its visible rows: the app's bind code is bounded per row (OS-cached file stats, cached SharedPreferences reads, string formatting, and thumbnail loads dispatched to background threads), so it cannot by itself occupy the main thread for 5 s at realistic visible-row counts, and the stack has no blocking primitive anywhere (no lock/wait/park, no binder transact, no file/network/database I/O frame). The main looper is demonstrably processing a frame-draw traversal at sample time, and the report's own background threads (DlnaSsdpListener,NanoHttpd Main Listener,pool-1-thread-1, HTTP-server threads) are all RUNNABLE — the >5 s block is device-side slowness / CPU starvation on a low-end TV during a normal RecyclerView layout, which the app cannot act on. TheAnrWatchdogThreadnow treats a main-thread stack whose top frame is a non-platform method with at least one more frame directly below it in the SAME class (the obfuscated bind/layout helper chain), with aRecyclerView.onLayoutframe and a frame-draw dispatch (Choreographer.doFrame/ViewRootImpl.performLayout/performTraversals), and no framework blocking primitive anywhere on the stack, as a false positive and resets its heartbeat instead of writing a report. Genuine freezes keep the main thread parked inside a blocking primitive (a lock, file/network/database I/O, or binder frame appears on the stack), or run app business logic outside a RecyclerView layout / frame-draw traversal, and are still reported. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread at the entry of an app Activity's own
onResumeoverride while the framework dispatches the Activity-resumed lifecycle event — top framejava.lang.StringBuilder.append/<init>(a single µs-scale O(n) buffer copy), directly under the Activity's ownonResume(a non-platform frame whose method name isonResume), underInstrumentation.callActivityOnResume→Activity.performResume→ActivityThread.performResumeActivity— reported from a Hisense HiSmart TV, SDK 30, app 1.8.1-GOOGLE. The sample caughtonResumeat its first string construction; the resume body is bounded (the storage-volume reload and device pings run in background coroutines, the rest are cached prefs reads and one-time dialogs), so a single append cannot by itself occupy the main thread for 5 s; the main looper is demonstrably processing a freshly dispatched lifecycle message (ActivityThread$H.handleMessage→servertransaction.ResumeActivityItem.execute) at sample time, which a thread parked inside a >5 s block cannot do — so the >5 s block is device-side slowness / CPU starvation on a low-end TV (the report's ownDefaultDispatcher-worker-*threads are BLOCKED on prefs/resource locks andqueued-work-looperis RUNNABLE doing a slowSharedPreferencesImpl.writeToFile→FileUtils.syncdisk sync, starving the main thread) or a post-stall sample of the backlog the main looper drains after a genuine stall. TheAnrWatchdogThreadnow treats a main-thread stack whose top frame isStringBuilder.append/<init>, whose second frame is a non-platformonResumeunder the framework Activity-resume lifecycle chain, with no framework blocking primitive anywhere on the stack, as a false positive and resets its heartbeat instead of writing a report. Genuine freezes keep the main thread inside blocking work — a lock, file/network I/O, or binder frame on the stack, or anonResumeexecuting a blocking call (the top frame is not a trivial StringBuilder construction) — and are still reported. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread inside a native compiled-XML asset read while a bundled-library checkbox view constructor loads its themed background during a RecyclerView row inflation in a normal frame — e.g. top frame
AssetManager.nativeOpenXmlAssetunderResources.getXml→ResourcesImpl.loadXmlResourceParser, reached fromandroidx.appcompat.widget.AppCompatCheckBox.<init>(the checkbox constructor inflating its own background drawable) via the frameworkLayoutInflater, under the app adapter's ViewHolder-inflation chain, underLinearLayoutManager/RecyclerView.onLayout, reached from a frame-draw traversal (Choreographer.doFrame→ViewRootImpl.performTraversals→performLayout) — reported from a SkyworthDigital UHD Google TV STB, SDK 34, app 1.8.1-GOOGLE. This is the RecyclerView-row-inflation counterpart of the already-filtered dialog-layout resource read (filter 3), reached from a view constructor viaResources.getXmlinstead ofDialog.show+Resources.getLayout. A single bounded native asset read of a small compiled XML drawable (the checkbox's themed background — the app already uses the lightweightAppCompatCheckBoxin its item layouts) runs once per inflated row and cannot by itself occupy the main thread for 5 s; the main looper is demonstrably processing a frame-draw layout traversal at sample time, so the >5 s block is device-side slowness / CPU starvation on a low-end TV or a post-stall sample of the backlog the looper drains after a genuine stall. TheAnrWatchdogThreadnow treats a main-thread stack whose top frame isAssetManager.nativeOpenXmlAsset/openXmlBlockAsset, with aResources.getXmlframe, anAppCompatCheckBox.<init>frame, aLayoutInflaterframe, aRecyclerView.onLayoutframe and a frame-draw dispatch (Choreographer.doFrame/ViewRootImpl.performLayout/performTraversals), and no framework blocking primitive anywhere on the stack, as a false positive and resets its heartbeat instead of writing a report. Genuine freezes keep the main thread parked inside a blocking primitive (a lock, file/network/database I/O, or binder frame), or reach the asset read from app business logic that is NOT a bundled-library checkbox constructor under a RecyclerView layout within a frame-draw traversal (e.g. heavyResources.loadDrawabledecoding, or aDialog.showwithout aRecyclerView.onLayoutframe), and are still reported. - Fixed an ANR (App Freeze) when the main thread is blocked inside
context.getFilesDir()— the framework'sFile.exists()/Linux.accessprivate-dir check — whileStorageBrowserActivity.onResumeruns the pending crash/ANR report check — reported from a SkyworthDigital UHD Google TV Stick, SDK 34, app 1.8.0-GOOGLE.CrashReportDialogHelper.maybeShowCrashReportDialog(invoked fromonResumeon the main thread) synchronously calledCrashReportManager.hasPendingReport/getPendingReportFile, whosegetReportDir()buildsFile(app.filesDir, "crash_reports")— everyapp.filesDiraccess runsgetFilesDir()→ensurePrivateDirExists()→File.exists(), and the check additionally runsmkdirs()+listFiles()+ areadText()of the report JSON, all on the main thread. On a slow or busy TV with concurrent background file I/O (the report's ownDefaultDispatcher-worker-*threads are RUNNABLE inFile.exists/FileInputStream.<init>), those syscalls exceeded the 5 s watchdog threshold and frozeonResume. The pending-report check + parse now runs onDispatchers.IOinside the suppliedlifecycleScope, returning to the main thread only to show the submit/discard dialog itself (extracted into ashowReportDialoghelper). Report detection, fingerprint dedup, deletion, and submission behaviour are unchanged — only the thread the file I/O runs on moved. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread inside the framework's
ViewPropertyAnimatoranimation-end chaining while a freshly dispatched Choreographer frame runs the animation clock — e.g. top frameandroid.view.ViewPropertyAnimator.getValue(the property readanimatePropertyperforms when a new alpha animation starts) underanimatePropertyunderViewPropertyAnimator.alpha, called by the app's animation-end listener (b2.run, an R8-obfuscated non-platform frame) dispatched fromViewPropertyAnimator$AnimatorEventListener.onAnimationEndafter aValueAnimator.endAnimation, reached fromAnimationHandler.doAnimationFrame/AnimationHandler$1.doFrameunder aChoreographer.doFramevsync dispatch (Choreographer$FrameDisplayEventReceiver.rununderHandler.handleCallback) — reported from an OPPO CPH1937, SDK 30, app 1.8.1-GOOGLE. The app chains its animations from the end-listener (e.g. a repeating fade/pulse: when one alpha animation ends, the listener starts the next.alpha()animation), which is bounded per-frame UI work —getValueis a µs-scale property read and starting the next animator is allocation plus a property getter, so neither can by itself occupy the main thread for 5 s. The main looper is demonstrably processing a freshly dispatched vsync frame callback at sample time, which a thread parked inside a >5 s block cannot do — the >5 s block is device-side slowness / CPU starvation on the mid-range OPPO (the report's ownDefaultDispatcher-worker-*,DlnaSsdpListener,NanoHttpd Main Listenerand HTTP-server threads are all RUNNABLE) or a post-stall sample of the backlog the main looper drains after a genuine stall. The stack has exactly one non-platform frame (the R8-obfuscated animation-end listener), so the pure-framework filter did not match. TheAnrWatchdogThreadnow treats a main-thread stack whose top frame is aViewPropertyAnimatormethod, with aViewPropertyAnimator$AnimatorEventListener.onAnimationEnddispatch from aValueAnimator.endAnimationreached through theAnimationHandler/Choreographervsync frame, a non-platform listener frame directly below theViewPropertyAnimator.alphacall, and no framework blocking primitive anywhere on the stack, as a false positive and resets its heartbeat instead of writing a report. Genuine freezes keep the main thread parked inside a blocking primitive (a lock, file/network/database I/O or binder frame), or run the animation-end listener's body doing blocking work, and are still reported. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread inside a bundled-library constructor chain while an Activity's own
onCreateruns during a framework-driven cold-start Activity launch — e.g.PackageInstallerActivity.onCreate→<obfuscated AppCompat superclass/delegate>.B→me2.<init>→xo8.<init>→lk1.<init>(top frame, the AppCompat delegate / superclass object graph the framework constructs when the Activity cold-starts), underActivity.performCreate→Instrumentation.callActivityOnCreate→ActivityThread.performLaunchActivity— reported from an SCBC R3, SDK 30, app 1.8.1-GOOGLE. The currently executing frame is one-time object construction (allocation, class loading, field init) that cannot by itself occupy the main thread for 5 s; the only app frame is the Activity's ownonCreatelifecycle callback the framework invoked, andPackageInstallerActivity'sonCreatebody is itself bounded (its heavy install work already runs onDispatchers.IO), so the >5 s block is the one-time framework-driven cold-start cost on a low-end device, which the app cannot act on. TheAnrWatchdogThreadnow treats a main-thread stack whose top frame is a<init>on a non-platform, non-app (bundled-library, R8-obfuscated) class, with an app frame that is an Activity's ownonCreate(every app frame an Activity class), a framework Activity cold-start launch frame (Activity.performCreate/Instrumentation.callActivityOnCreate/ActivityThread.performLaunchActivity), and no framework blocking primitive anywhere on the stack, as a false positive and resets its heartbeat instead of writing a report. Genuine freezes keep the main thread inside app business logic — a top frame that is not a bundled-library<init>under an ActivityonCreate(e.g. the Activity's ownonCreateor a helper it calls directly, whose class starts with the app package), an app frame that is not an Activity class (e.g. adapter bind code or a repository), or a framework blocking primitive (a lock, file/network/database I/O, or binder frame) — and are still reported. - Fixed a false-positive ANR (App Freeze) report when the ANR watchdog samples the main thread inside a
bindService/bindIsolatedServicecall routed through a device-injected service-hook proxy — e.g. Google Play's injected license check (com.pairip.licensecheck.LicenseClient.connectToLicensingService) invoking the one-lineContextImpl.bindServicefrom a main-looper Runnable (Handler.handleCallback), which the vendor ROM's virtual-service hook (com.vlite.sdk, wrapping the ActivityManager binder in a dynamic$Proxy5) redirects into its own binder round-trip ($Proxy5.bindIsolatedService→com.vlite.sdk...virtualservice.am.j$b$a.initProcess→BinderProxy.transact→transactNative, top frame) — reported from a Xiaomi Redmi K20 Pro, SDK 29, app 1.7.7-GOOGLE. The app merely invoked the framework bind API; the >5 s block is the vendor virtual service's binder response latency, which the app cannot act on (the stack has zeroza.kilowatch.ultimatefilemanagerframes). This is the bind-direction counterpart of the already-filteredunbindServiceshape. TheAnrWatchdogThreadisServiceConnectionBinderStallfilter (8) now also treats a main-thread stack whose top frame isBinderProxy.transact/transactNative, containing aContextImpl.bindService/bindServiceCommonframe, abindIsolatedServiceframe, ajava.lang.reflect.Proxy.invokeframe, andcom.vlite.sdkframes, as a false positive and resets its heartbeat instead of writing a report. The bind direction requires the hook-proxy signature so a genuine freeze where app business logic binds a slow target service and blocks is still reported. - Fixed a crash (
java.lang.VerifyError— "Verifier rejected classio.netty.util.internal.PlatformDependent...estimateMaxDirectMemory()failed to verify") when starting the embedded Ktor/Netty file servers on devices whose ART verifier rejects the R8-optimized Netty bytecode — reported from a HONOR ELI-NX9, SDK 36 (Android 16), app 1.8.1-GOOGLE, on thefile-server-startbackground thread duringNettyApplicationEngine.start→ Ktor's channel-class probe (KQueue.<clinit>→Native.<clinit>→ClassInitializerUtil.tryLoadClasses). Netty'sPlatformDependentfailed verification only under that device's stricter runtime; becauseVerifyErroris anError(not anException), the existingcatch (e: Exception)guards did not contain it and the uncaught error crashed the app. Every embedded-server boot boundary now catchesLinkageError(the parent ofVerifyError,ExceptionInInitializerError,NoClassDefFoundError) so a verifier rejection degrades to a logged, user-visible failure instead of a crash:RemoteManageActivity.startServer()shows the existing server-error UI,FileServer.start()logs and leaves the server stopped,WebShareServer.start()returns an empty URL which the Premium Share activities (mobile + TV) surface with an error toast and close. ProGuard also now keepsInnerClasses,EnclosingMethod,Signatureattributes for the reflection/MethodHandle-heavy Ktor/Netty stack (Netty's official Android setup requirement) to help R8 produce verifier-safe bytecode.