From 289be6b142915ec3082e230120edd97162596fd4 Mon Sep 17 00:00:00 2001 From: Alexey Karimov Date: Tue, 29 Sep 2026 18:13:43 +0500 Subject: [PATCH 1/4] docs(android): 7.3.0 release notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the 7.3.0 section to the Android release notes: the internal-log security fix, absent report outputs, crash-payload accounting, pending-report limits, the background low-memory option, the public network sanitizer, video fixes, and the behaviour changes (report handlers off the main thread, logException(null), pending-report limits on by default). Also documents the options new in 7.3.0 in the configuration reference and on the exits page, Bugsee.getHostLaunchOptions(), enum names in the launch map, and the 60 s report-handler chain cap. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Change-Id: Ia718fed6e610190069baef93c23a03eca22d35be --- docs/sdk/android/configuration/overview.mdx | 12 +- docs/sdk/android/issue-detection/exits.mdx | 2 + docs/sdk/android/release-notes.md | 337 ++++++++++++++++++++ 3 files changed, 348 insertions(+), 3 deletions(-) diff --git a/docs/sdk/android/configuration/overview.mdx b/docs/sdk/android/configuration/overview.mdx index 63511e7..c7ee62f 100644 --- a/docs/sdk/android/configuration/overview.mdx +++ b/docs/sdk/android/configuration/overview.mdx @@ -33,10 +33,13 @@ Every Bugsee SDK option can be set in two equivalent ways. The literal string ke | `Duration` | `com.bugsee.option.config.duration` | int (seconds) | `60` | Maximum length of the cyclical capture buffer (video, logs, breadcrumbs); older data rolls off once the window is full. | | `WifiOnlyUpload` | `com.bugsee.option.config.wifi-only-upload` | boolean | `false` | Defer report uploads until the device is on Wi-Fi. | | `ReportProcessingInProcess` | `com.bugsee.option.config.report-processing-in-process` | boolean | `true` | When `true`, report export and upload run in-process on background threads; when `false` they go through Android's `JobScheduler` so they survive process death, at the cost of upload latency. | -| `ReportHandlerCallbackTimeout` | `com.bugsee.option.config.report-handler-callback-timeout` | int (seconds) | `30` | How long to wait for a `ReportHandler` completion callback before auto-firing it so the report isn't lost. `0` disables the timeout. Not applied to terminating (crash) sequences. | +| `ReportHandlerCallbackTimeout` | `com.bugsee.option.config.report-handler-callback-timeout` | int (seconds) | `30` | How long to wait for a `ReportHandler` completion callback before auto-firing it so the report isn't lost. `0` disables the per-handler timeout; since 7.3.0 the whole handler chain for one callback is still capped at 60 s (or at this timeout, if longer). Not applied to terminating (crash) sequences. | | `NotifyFlushDelay` | `com.bugsee.option.config.notify-flush-delay` | int (ms) | `0` | Coalescing window before the [notification relay](/sdk/android/notification-relay) queue drains; `0` starts the drain as soon as a notification is persisted. Only affects the non-urgent, batched drain — not an urgent `notify()` skip-ahead POST. | +| `MaxDataSize` | `com.bugsee.option.config.max-data-size` | int (MB) | `150` | Maximum disk space that reports waiting to be sent may occupy. When exceeded, whole pending reports are deleted — errors first, then bug reports, then crashes, oldest first within each — until they fit. The newest crash and any report being exported or uploaded are never deleted. Values below `10` are treated as `10`. *(7.3.0)* | +| `MaxPendingReports` | `com.bugsee.option.config.max-pending-reports` | int | `30` | Maximum number of reports waiting to be sent; over it, pending reports are deleted in the same order as for `MaxDataSize`. Values below `1` are treated as `1`. *(7.3.0)* | +| `MaxPendingReportAge` | `com.bugsee.option.config.max-pending-report-age` | int (days) | `30` | Maximum age of a report waiting to be sent; older reports are deleted whatever their type. `0` or a negative value turns aging off. *(7.3.0)* | -`Duration` caps the cyclical capture buffer (video, logs, breadcrumbs) at the given number of seconds. `WifiOnlyUpload` defers report uploads until Wi-Fi. `ReportProcessingInProcess = false` routes report processing through `JobScheduler` so it survives process death (at the cost of upload latency). `ReportHandlerCallbackTimeout = 0` disables the timeout — but it doesn't apply to crash sequences. `NotifyFlushDelay` delays the notification-relay drain so nearby `Bugsee.notify(...)` calls can batch together. +`Duration` caps the cyclical capture buffer (video, logs, breadcrumbs) at the given number of seconds. `WifiOnlyUpload` defers report uploads until Wi-Fi. `ReportProcessingInProcess = false` routes report processing through `JobScheduler` so it survives process death (at the cost of upload latency). `ReportHandlerCallbackTimeout = 0` disables the per-handler timeout (the 60 s chain cap still applies since 7.3.0) — and the timeout doesn't apply to crash sequences. `NotifyFlushDelay` delays the notification-relay drain so nearby `Bugsee.notify(...)` calls can batch together. @@ -80,6 +83,7 @@ The full set of detection options is documented on each detector's own page in [ | `DetectFrustration` | `com.bugsee.option.detect.frustration` | boolean | `false` | Detect user-frustration patterns (rage taps, dead taps, stuck loading, frozen UI) and report them as simulated errors grouped by screen and element. | | `DetectAndReportExit` | `com.bugsee.option.detect.exit` | boolean | `true` | Detect and report application exits. The per-reason flags below select which exit reasons are reported. | | `DetectAndReportExitLowMemory` | `com.bugsee.option.detect.exit.low_memory` | boolean | `true` | Report exits where the OS killed the app to reclaim memory. | +| `DetectAndReportExitLowMemoryBackgroundAsError` | `com.bugsee.option.detect.exit.bg_low_memory_as_error` | boolean | `false` | Report a low-memory kill of a process that was not in the foreground (importance foreground or foreground service) as a non-fatal error instead of a crash. Foreground kills stay crashes. Applies only on devices that report low-memory kills; elsewhere a background kill is already a non-fatal error. Requires `DetectAndReportExitLowMemory`. *(7.3.0)* | | `DetectAndReportExitNotResponding` | `com.bugsee.option.detect.exit.not_responding` | boolean | `true` | Report exits caused by the app not responding (ANR). | | `DetectAndReportExitNotRespondingAsCrash` | `com.bugsee.option.detect.exit.not_responding.as_crash` | boolean | `false` | Report ANR exits as crashes rather than as exits, so they group with crashes in the dashboard. Requires `DetectAndReportExitNotResponding`. *(7.1.0)* | | `DetectAndReportAnrSampling` | `com.bugsee.option.detect.anr.sampling` | boolean | `true` | Sample the main thread's stack during an ANR and attribute the report to the derived culprit frames rather than to a single snapshot taken once the OS confirms the ANR. Only has effect when `DetectAndReportExitNotResponding` is enabled. *(7.2.0)* | @@ -235,7 +239,7 @@ See [Performance monitoring](/sdk/android/performance) for the APM overview. ## Enum reference -Enum-typed options accept the enum constant programmatically, or the enum value **name** as a string in the manifest (for example, `android:value="Blocker"` for `IssueSeverity.Blocker`). +Enum-typed options accept the enum constant programmatically, or the enum value **name** as a string in the manifest (for example, `android:value="Blocker"` for `IssueSeverity.Blocker`). Since 7.3.0 the launch map accepts the name as a string too, matched case-insensitively. An integer is not accepted in place of an enum constant in the map. | Enum | Values | | --- | --- | @@ -264,6 +268,8 @@ val opts: OptionsContainer = Bugsee.getLaunchOptions() val duration: Int = opts.getOption(Options.Duration, 60) ``` +`Bugsee.getHostLaunchOptions()` *(7.3.0)* returns only the options your app supplied — through `Bugsee.launch(...)` or manifest `` — without the SDK defaults merged in, as an unmodifiable `Map`. Only option keys the SDK recognizes are included, with the values it accepted. The map is replaced, not merged, by the next `launch()` or `relaunch()`. + ## Worked examples ### Manifest-only configuration diff --git a/docs/sdk/android/issue-detection/exits.mdx b/docs/sdk/android/issue-detection/exits.mdx index 3a72b10..b6c724b 100644 --- a/docs/sdk/android/issue-detection/exits.mdx +++ b/docs/sdk/android/issue-detection/exits.mdx @@ -70,6 +70,8 @@ Bugsee.launch(this, "", options); | Other | `DetectAndReportExitOther` | `com.bugsee.option.detect.exit.other` | `false` | | Unknown | `DetectAndReportExitUnknown` | `com.bugsee.option.detect.exit.unknown` | `false` | +By default, low-memory kills are reported as crashes. Background low-memory kills are routine on modern Android, so counting them as crashes can inflate your crash rate; set `DetectAndReportExitLowMemoryBackgroundAsError` (`com.bugsee.option.detect.exit.bg_low_memory_as_error`, default `false`, new in 7.3.0) to report a kill of a process that was not in the foreground as a non-fatal error instead. Foreground kills stay crashes. The option applies only on devices that report low-memory kills; elsewhere a background kill is already reported as a non-fatal error. + The reasons map directly onto `ApplicationExitInfo.getReason()` codes — see [the platform reference](https://developer.android.com/reference/android/app/ApplicationExitInfo) for the upstream documentation. ## See also diff --git a/docs/sdk/android/release-notes.md b/docs/sdk/android/release-notes.md index bddefe2..e5959f6 100644 --- a/docs/sdk/android/release-notes.md +++ b/docs/sdk/android/release-notes.md @@ -7,6 +7,343 @@ slug: "/sdk/android/release-notes" Release history for Bugsee Android SDK 7.x. Looking for the previous major version? See the [6.x release notes](/sdk/android/v6/release-notes). See the [migration guide](/sdk/android/migration) when planning your upgrade from 6.x. +## 7.3.0 + +A security and reliability release. It stops the SDK's own diagnostic log from carrying your app's +request URLs and user values into reports. Reports now say which of their parts is missing and why, +instead of leaving it out silently. Several causes of broken report video are fixed. New options +limit how much unsent report data the SDK keeps on the device. + +**Security** + +- **The SDK's internal log no longer records your app's URLs or user values.** In 7.0.0-beta1 + through 7.2.0, the SDK's own diagnostic log (the "Internal log" attached to every report) could + include your app's HTTP request URLs, with any embedded credentials (`user:password@`) and full + query strings. They came from the HttpURLConnection, OkHttp, Ktor and WebView failure paths, and + from raw logcat lines of your app that the SDK could not parse. The same log also recorded: + - the values passed to `Bugsee.setUserIdentifier()` and `Bugsee.setAttribute()`; + - the names of `http.client` performance transactions, which contain URLs; + - the `toString()` output and exception messages of filters and listeners your app registers. + + These log lines now carry no data from your app, only class names, ids and counts. As a second + layer, every internal-log record is sanitized when it is written: credentials are stripped from + URLs and sensitive query values are redacted. Records written by an earlier SDK build are deleted + on the first launch after the upgrade and are never attached to a report; anything that cannot + be deleted is left out of reports. + + The data went only into the reports uploaded to your own Bugsee project; no third party received + it. The 6.x SDK is not affected. We recommend upgrading any app on an affected 7.x version. + + :::note[Known limits] + - Records written by an older build *after* 7.3.0 has run (a downgrade followed by another + upgrade) are not deleted. + - Reports that were already bundled before the upgrade and are still waiting to upload are sent + as they are. + - With the `$$DEBUG` option on, log lines are echoed to logcat before they are sanitized. + - Option values the SDK rejects at launch are still logged as given. + - **React Native:** native code that uses HttpURLConnection, and the fallback path of + `react-native-webview`, were affected as described above. JavaScript `fetch` and + `XMLHttpRequest` run on OkHttp and are captured only when `bugsee-android-okhttp` is in the + app; there the exposure was at most a host name, and only when Bugsee's own event handling + failed. + ::: + +**New features** + +- **Reports say what is missing, and why.** When a part of a report could not be delivered — the + video, a screenshot, a capture stream or the crash details — the report now lists it as absent, + with a reason, instead of leaving it out silently. In this release that happens when a file is + lost while the report is being bundled (reason `export_failed`), and when a crash or error report + has no crash details at all (see the crash-report fix below). Attachments are never listed as + absent. The Bugsee servers already accept the new form. + +- **Limits on reports waiting to be sent.** Reports that have not been uploaded yet used to + accumulate on the device without bound. Three new options cap them: + + | `Options` constant | Manifest key | Default | + | --- | --- | --- | + | `MaxDataSize` | `com.bugsee.option.config.max-data-size` | `150` (MB, minimum `10`) | + | `MaxPendingReports` | `com.bugsee.option.config.max-pending-reports` | `30` (minimum `1`) | + | `MaxPendingReportAge` | `com.bugsee.option.config.max-pending-report-age` | `30` (days; `0` or less turns aging off) | + + Reports older than the age limit are deleted whatever their type. Over the size or count limit, + whole reports are deleted — errors first, then bug reports, then crashes, oldest first within + each — until the rest fit. The newest crash and any report being exported or uploaded are never + deleted. The check runs at launch, before each new report is exported, and when free disk space + runs low. + + :::note + The limits are on by default. A device that stays offline for more than 30 days, or collects + more than 30 unsent reports, now loses the oldest ones instead of keeping them all. + ::: + +- **Report background low-memory kills as errors.** Background low-memory kills are routine on + modern Android — cached, frozen or push-woken processes are reclaimed all the time — so counting + them as crashes can inflate your crash rate. Play Console's user-perceived low-memory metric + counts foreground kills only. The new `DetectAndReportExitLowMemoryBackgroundAsError` option + (`com.bugsee.option.detect.exit.bg_low_memory_as_error`, default `false`) reports a low-memory + kill of a process that was not in the foreground as a non-fatal error. A process counts as + foreground when its importance at death was foreground or foreground service. Foreground kills + are reported as crashes either way. The option applies only on devices that report low-memory + kills; on the others a background kill is already reported as a non-fatal error. + +- **A public network sanitizer for your network filter.** A network filter replaces the built-in + redaction of network events. To keep that redaction inside your filter, call + `Bugsee.getDefaultNetworkSanitizer().sanitize(event)`. The method returns a stateless + `NetworkDataSanitizer` (package `com.bugsee.library.contracts.exchange`) that is safe from any + thread and works before `Bugsee.launch()`, so you can also use it in a manifest-declared filter. + It also has `sanitizeUrl(event)` and `sanitizeErrorMessage(event)`. See + [Privacy → Network traffic](/sdk/android/privacy/network#a-filter-replaces-the-built-in-redaction). + The static `com.bugsee.library.shared.security.privacy.NetworkDataSanitizer` class is deprecated + but keeps working. + +- **See the options your app passed at launch.** `Bugsee.getHostLaunchOptions()` returns the + options your app supplied to `Bugsee.launch()` or in manifest ``, without the SDK + defaults merged in, so you can tell "the app asked for this" apart from "this is the default". + Only option keys the SDK recognizes are included, with the values it accepted. The map is + replaced, not merged, by the next `launch()` or `relaunch()`. `Bugsee.getLaunchOptions()` still + returns the options in effect. + +- **Attach files without loading them into memory.** In a report handler, + `Report.addAttachment(File file, String name, String mimeType, boolean move)` moves or copies a + file into the report, so large logs and database dumps are fine. It returns `null` and leaves the + file where it is when it cannot be added. `Report.addAttachment(byte[] data, String name, String + mimeType)` adds small in-memory content. A report now refuses new attachments, including through + `createAndAddAttachment()`, once its handler has completed or it has been submitted, since they + would never reach the upload. + +- **Annotated screenshots are marked as such.** A screenshot is now marked annotated only when the + user drew on it in the screenshot editor, as on iOS. Before, Android sent no annotation flag, so + every Android screenshot was treated as annotated and shown over the video. Your own code can + set the flag with `Report.setScreenshot(displayId, bitmap, annotated)` and read it with + `Report.isScreenshotAnnotated(displayId)`. `Report.getScreenshotDisplayIds()` now returns the + display ids in ascending order. + +- **Fold state on foldable devices.** On foldable devices, reports now carry `fold_state` and + `hinge_angle` system traces: the posture (closed, partially open or fully open), the bounds of + each fold, and the hinge angle (API 30 and newer). No new dependency is needed; if your app + already bundles `androidx.window` 1.1 or newer, the SDK can use it. + +- **Stylus tilt and direction.** Stylus and eraser touches now record the pen's tilt and the + direction it points, in the same form the iOS SDK uses, when the device reports those axes. + +**Fixes** + +- **Report videos no longer show green, stale or blended frames.** With software video encoders + on API 29 and newer, the encoder could read a frame before the SDK's copy of it had fully + arrived, and encoded empty memory (green), an earlier frame, or a mix of the two. In testing + this affected about one frame in five on an API 30 emulator. The SDK now feeds those encoders + through an input surface, and falls back to the previous method if that fails. Separately, + reports recovered right after an early crash could come out entirely green, because the video + code they needed was not loaded yet; it is now loaded before any export. A failed encoding pass + is now retried instead of shipping the report without video. + +- **Video no longer garbles or freezes at capture boundaries.** Each captured frame is stored as a + change against the frame before it. At a boundary between capture segments, a frame could be + stored against a base that sat in the previous segment, or be matched with the wrong base, and + the exported video showed garbled or frozen frames. A frame that opens a segment is now always a + full frame, frame times always increase, and export skips a frame whose base is missing instead + of decoding it into garbage. + +- **Frames right after a rotation are drawn in the new orientation.** For up to a few seconds after + the device rotated, video frames could show the old orientation's window cropped into the new + one, because the app had not yet laid its windows out again. The capture now holds the last frame + until the windows catch up, as the screen itself does during a rotation, for at most 5 seconds + per rotation. This applies to the default PixelCopy capture (`VideoMode.V2`). + +- **Touches line up with the video from the first frame.** A recording whose frames never needed + padding carried no video geometry at all, so the viewer had no screen size to place + touches against. Geometry is now recorded on the first frame and on every rotation. + +- **One stuck report no longer blocks every other report.** Reports are exported one at a time. + On Android 6, a video whose frame times went backwards could deadlock the encoder, and no report + was delivered again on any later launch. Frame times now always increase, a watchdog abandons a + stuck video so the report ships without it, and a report that keeps failing is exported without + video after three attempts and deleted after two more. Pending reports are now processed oldest + first. + +- **Crash reports always account for their crash details.** If the SDK cannot write a report's + crash details (for example under the memory pressure an `OutOfMemoryError` leaves behind), it + retries once with a minimal version: the exception class, message, top 32 frames and signature, + without the thread dump or cause chain. If that fails too, the report lists the crash details as + absent (`export_failed`). A crash or error report that reaches upload with no crash details at + all — for example, one recovered after the process died before they were written — lists them + as absent with reason `unknown`. The SDK now also saves the report as soon as its crash + details are registered, which narrows that window. `Bugsee.logException()` and `Bugsee.sendBugReport()` now + do this disk work off the main thread. + + :::note[Behavior change] + `Bugsee.logException(null)` now reports a simulated exception, built on the thread that called + it. On the dashboard these reports now group by call site, instead of all landing in one + `` issue. + ::: + +- **Report handlers no longer run on the main thread.** A hang or an ANR *is* a blocked main + thread, so handlers dispatched there could not run for exactly the reports where they matter + most. Handlers for a healthy report now run on a dedicated SDK background thread that has a + `Looper`, so `new Handler()` and `Toast` still work there. The thread is started only when a + report actually has a handler to run. The wrapper and app handlers for one callback are now + capped together at 60 seconds (or one `ReportHandlerCallbackTimeout`, if that is longer), so a + `ReportHandlerCallbackTimeout` of `0` no longer lets a handler hold a report forever. During + early-crash recovery at launch, handlers are now told `isTerminating = false`, because that + process is alive. + + :::caution[Behavior change] + If your `ReportHandler` touches views or other UI state, post that work to the main thread + yourself, for example with `new Handler(Looper.getMainLooper()).post(...)`. On the crash paths + the SDK still prefers a background thread, but when it cannot get one the handler runs on the + thread finishing the report, which during early-crash recovery can be the main thread; keep + handlers short there. + ::: + +- **Recovered crashes are reported against the build that crashed.** Native crashes and exits the + system reports (ANRs, low-memory kills and the like) are turned into reports on the next launch. + After an app update, they were attributed to the new version. Each capture session now records + its app and SDK versions, and a recovered report uses those. A race at launch could also delete + the dead session's capture data, so some recovered reports carried no version at all and were + rejected by the server. Recovered reports now keep that session's capture, and always carry a + version; when none was recorded, they use the running app's version. + +- **A second crash on another thread no longer loses the first.** If another thread crashed while + the SDK was still saving a crash, the process was killed mid-save and the first report was lost — + common under memory pressure, where secondary thread deaths follow an `OutOfMemoryError`. The + second crash now waits, for at most 15 seconds, until the first is saved. + +- **Network events are no longer dropped after a clock change.** If the device clock was corrected + while the app was running, as on a phone that boots offline and gets network time later, every + network event from OkHttp, Ktor, Cronet and the Android 14 `HttpEngine` was silently dropped for + the rest of the process. These events now use the SDK's own clock. If you create network events + yourself with `Bugsee.getExchangeFactory()`, use the new + `BugseeExchangeFactory.currentTimestamp()` for their timestamps. + +- **Breadcrumbs and long transactions are no longer dropped.** A breadcrumb passed to + `Bugsee.addBreadcrumb()` as your own `Breadcrumb` implementation lost its timestamp and was + discarded, and a breadcrumb that a breadcrumb filter returned after a delay could be discarded + too. A performance transaction that outlasted the current + one-second capture segment was left out of report capture as well. + +- **Report delivery is more reliable.** + - A report whose capture data could not be exported, for example on a full disk, was deleted with + its crash details, screenshot and attachments. It is now sent with what it has. + - When two processes of the same app exported the same report at once, the uploaded report could + name one attempt's files while containing the other's, and the server then dropped all of its + capture data. One process now publishes each report. + - A report is bundled from exactly the files it lists, so a retried export no longer uploads the + failed attempt's video and data as well. + - The same pending report is no longer exported twice at launch. + - A report could contain half-written capture entries, which cost video frames and performance + data. It now copies only what the SDK finished writing. + - Starting capture with a large backlog of pending reports could run out of memory on devices + with a small heap. + - An empty or unreadable capture segment no longer corrupts a whole exported log or event + stream. + +- **Rare crashes and hangs caused by the SDK are fixed.** + - A race while screen capture was stopping or relaunching could crash the app with a + `NullPointerException` on a Bugsee capture thread, or leave the session recording black video + until the next restart. + - Two report exports running at the same time could crash the app with a + `ConcurrentModificationException`. + - On a device whose Android 16 framework lacks the satellite API, capture start crashed the app + with a `NoClassDefFoundError`. An SDK trace that fails to start is now left out instead. + - An exception in an SDK background task could reach the default handler and kill the app, or + silently stop a periodic job (the APM upload, the frozen-UI and stuck-loading detectors) for + the rest of the session. + - If loading the SDK's preferences failed or stalled, the app could crash, or the main thread + could wait long enough for an ANR. + - Stopping the SDK while the first frame was being drawn could crash the main thread. + +- **The Ktor extensions compile again.** In the published 7.2.0 `bugsee-android-ktor-2` and + `bugsee-android-ktor-3` artifacts, `install(BugseeKtor2Plugin)`, `install(BugseeKtor3Plugin.Plugin)` + and the `bugseeWebSocket` helpers did not compile in Kotlin apps, because code shrinking had + renamed or removed them. + +- **Enum options can be passed by name in the launch map.** An enum option passed to + `Bugsee.launch(context, token, options)` as a string naming the constant (for example `"High"` + for `VideoQuality`) was accepted but never converted, so the option could fail when the SDK read it. Names are now matched + case-insensitively, the same as in the manifest. An integer is still not accepted in place of an + enum constant. + +- **Leak detection no longer leaks.** The `bugsee-android-leak` extension held on to the views of + destroyed fragments — between 2.5 and 21 MB in testing. Fragments destroyed together with their + activity were never watched for leaks at all; they are now. + +- **Anomaly issues no longer split by port.** An explicit port in a URL was part of the anomaly + grouping key, so a local or development server on a new port each run opened a new issue each + time. Stale endpoints could also fill the detector's baselines and leave live endpoints without + one; the least recently used ones are now evicted. + + :::note + Existing anomaly issues whose URL had an explicit port regroup once under the port-free key. + ::: + +- **HTTP-error reports for different endpoints no longer suppress each other.** Their + de-duplication key could collide, so a report for one endpoint silenced another's. + +- **Filters that misbehave are easier to spot.** A log or network filter that never calls its + callback now logs one warning per process (the events it holds are still dropped). A filter that + calls its callback and then throws no longer corrupts the event it returned. + +**Privacy** + +- **Filters set before launch apply to data captured at launch.** A log, network or breadcrumb + filter set before `Bugsee.launch()`, or declared in the manifest, was not yet in force for the + data captured while launch was starting up, so that data was recorded unfiltered. Manifest + filters are now installed before capture starts. + +- **Removing the only screenshot in the bug-report dialog removes it from the report.** It was + hidden in the dialog but still uploaded, and, if the user had drawn on it, shown on the issue as + annotated. + +- **Secure fields are no longer named in breadcrumbs, detections or the view hierarchy.** A secure + field's pixels, touches and keys were withheld, but its identity was not: its id and tag reached + breadcrumbs (for example `ui.click` and `focus.set`), its id reached rage-tap reports, and its + resource name reached the view-hierarchy dump. Breadcrumbs now leave out the id and tag and carry + `view.secure: true`, rage-tap reports show `` instead, and the dump leaves out the + resource name. The view class is kept. + +- **URL credentials are stripped wherever the URL is reported.** The built-in network redaction now + also removes `user:password@` from the captured request URL and from error messages, and the + performance transaction names and `http.url` attributes drop them too. HTTP-error reports strip + them whether or not a network filter is installed; before, only with one. + +- **A truncated body is no longer recorded unredacted.** A JSON body too large to capture in full + could be recorded cut short, and cut-off JSON cannot be parsed for redaction, so it was recorded + as it was. Such a + body is now left out and marked as too large. The built-in redaction also covers network error + messages now. + +- **More fields are recognized as secure.** The built-in rule for Stripe, Braintree and card.io + payment widgets now reaches their input fields, not only their containers. View-based fields + with the `creditCardExpirationDay` or `newPassword` autofill hint are now secure, as they already + were in Compose. Keys such as `KEYCODE_RO` and `KEYCODE_YEN`, which type characters on Brazilian + and Japanese keyboards, are now redacted like other character keys. + +- **A warning when Compose masking is missing.** Compose password fields are masked only by the + `bugsee-android-compose` extension. The SDK now logs a warning once per process when it sees + Compose UI on screen and that extension is not in the app. + +**Compatibility** + +- New public API: `Bugsee.getDefaultNetworkSanitizer()` and the `NetworkDataSanitizer` interface, + `Bugsee.getHostLaunchOptions()`, `Report.addAttachment(...)` (two overloads), + `Report.setScreenshot(displayId, bitmap, annotated)` and its async variant, + `Report.isScreenshotAnnotated()`, `BugseeExchangeFactory.currentTimestamp()`, and the + `MaxDataSize`, `MaxPendingReports`, `MaxPendingReportAge` and + `DetectAndReportExitLowMemoryBackgroundAsError` options. The new `Report` methods have default + implementations, so your own `Report` implementations keep compiling. + +- `com.bugsee.library.shared.security.privacy.NetworkDataSanitizer` is deprecated. If your code + wildcard-imports both `com.bugsee.library.contracts.exchange.*` and + `com.bugsee.library.shared.security.privacy.*`, the compiler reports `NetworkDataSanitizer` as + ambiguous after the upgrade. Import the one you mean by name, or use + `Bugsee.getDefaultNetworkSanitizer()`, which needs no import. + +- A few members of internal SDK packages that apps are not meant to call were removed from the + published API listing. The `FeedbackActivity.showActivity()` and `isShown()` companion methods + were listed as public but were never present in the published artifact; use + `Feedback.showFeedbackActivity()`. + ## 7.2.0 A feature release. Your app can now send a message straight to your Slack or Teams integration From 6417bc9fc3b6eadf42d28f265fe1260dc4f995e3 Mon Sep 17 00:00:00 2001 From: Alexey Karimov Date: Tue, 29 Sep 2026 18:14:17 +0500 Subject: [PATCH 2/4] docs(android): reflow long lines in the 7.3.0 notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Change-Id: I345f4405f4740cc5aef948d6b2957c6297bf4965 --- docs/sdk/android/release-notes.md | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/docs/sdk/android/release-notes.md b/docs/sdk/android/release-notes.md index e5959f6..bf74424 100644 --- a/docs/sdk/android/release-notes.md +++ b/docs/sdk/android/release-notes.md @@ -152,8 +152,8 @@ limit how much unsent report data the SDK keeps on the device. per rotation. This applies to the default PixelCopy capture (`VideoMode.V2`). - **Touches line up with the video from the first frame.** A recording whose frames never needed - padding carried no video geometry at all, so the viewer had no screen size to place - touches against. Geometry is now recorded on the first frame and on every rotation. + padding carried no video geometry at all, so the viewer had no screen size to place touches + against. Geometry is now recorded on the first frame and on every rotation. - **One stuck report no longer blocks every other report.** Reports are exported one at a time. On Android 6, a video whose frame times went backwards could deadlock the encoder, and no report @@ -168,9 +168,9 @@ limit how much unsent report data the SDK keeps on the device. without the thread dump or cause chain. If that fails too, the report lists the crash details as absent (`export_failed`). A crash or error report that reaches upload with no crash details at all — for example, one recovered after the process died before they were written — lists them - as absent with reason `unknown`. The SDK now also saves the report as soon as its crash - details are registered, which narrows that window. `Bugsee.logException()` and `Bugsee.sendBugReport()` now - do this disk work off the main thread. + as absent with reason `unknown`. The SDK now also saves the report as soon as its crash details + are registered, which narrows that window. `Bugsee.logException()` and `Bugsee.sendBugReport()` + now do this disk work off the main thread. :::note[Behavior change] `Bugsee.logException(null)` now reports a simulated exception, built on the thread that called @@ -219,8 +219,8 @@ limit how much unsent report data the SDK keeps on the device. - **Breadcrumbs and long transactions are no longer dropped.** A breadcrumb passed to `Bugsee.addBreadcrumb()` as your own `Breadcrumb` implementation lost its timestamp and was discarded, and a breadcrumb that a breadcrumb filter returned after a delay could be discarded - too. A performance transaction that outlasted the current - one-second capture segment was left out of report capture as well. + too. A performance transaction that outlasted the current one-second capture segment was left + out of report capture as well. - **Report delivery is more reliable.** - A report whose capture data could not be exported, for example on a full disk, was deleted with @@ -260,9 +260,9 @@ limit how much unsent report data the SDK keeps on the device. - **Enum options can be passed by name in the launch map.** An enum option passed to `Bugsee.launch(context, token, options)` as a string naming the constant (for example `"High"` - for `VideoQuality`) was accepted but never converted, so the option could fail when the SDK read it. Names are now matched - case-insensitively, the same as in the manifest. An integer is still not accepted in place of an - enum constant. + for `VideoQuality`) was accepted but never converted, so the option could fail when the SDK read + it. Names are now matched case-insensitively, the same as in the manifest. An integer is still + not accepted in place of an enum constant. - **Leak detection no longer leaks.** The `bugsee-android-leak` extension held on to the views of destroyed fragments — between 2.5 and 21 MB in testing. Fragments destroyed together with their @@ -309,9 +309,8 @@ limit how much unsent report data the SDK keeps on the device. - **A truncated body is no longer recorded unredacted.** A JSON body too large to capture in full could be recorded cut short, and cut-off JSON cannot be parsed for redaction, so it was recorded - as it was. Such a - body is now left out and marked as too large. The built-in redaction also covers network error - messages now. + as it was. Such a body is now left out and marked as too large. The built-in redaction also + covers network error messages now. - **More fields are recognized as secure.** The built-in rule for Stripe, Braintree and card.io payment widgets now reaches their input fields, not only their containers. View-based fields From c8937388fae6a519b9bf5ef5ff6d0e3871cea158 Mon Sep 17 00:00:00 2001 From: Alexey Karimov Date: Tue, 29 Sep 2026 18:19:38 +0500 Subject: [PATCH 3/4] docs(android): keep only customer-facing changes in the 7.3.0 notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes implementation details, internal names and test figures from the 7.3.0 section and from the option descriptions added for 7.3.0, per owner review. Each item now states the fixed symptom, the new API or option, or the behaviour change a customer must act on. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Change-Id: Ia4cc90330515445867c73f6ccfdc2f2d345ee810 --- docs/sdk/android/configuration/overview.mdx | 14 +- docs/sdk/android/issue-detection/exits.mdx | 2 +- docs/sdk/android/release-notes.md | 403 +++++++------------- 3 files changed, 154 insertions(+), 265 deletions(-) diff --git a/docs/sdk/android/configuration/overview.mdx b/docs/sdk/android/configuration/overview.mdx index c7ee62f..2d6b579 100644 --- a/docs/sdk/android/configuration/overview.mdx +++ b/docs/sdk/android/configuration/overview.mdx @@ -33,13 +33,13 @@ Every Bugsee SDK option can be set in two equivalent ways. The literal string ke | `Duration` | `com.bugsee.option.config.duration` | int (seconds) | `60` | Maximum length of the cyclical capture buffer (video, logs, breadcrumbs); older data rolls off once the window is full. | | `WifiOnlyUpload` | `com.bugsee.option.config.wifi-only-upload` | boolean | `false` | Defer report uploads until the device is on Wi-Fi. | | `ReportProcessingInProcess` | `com.bugsee.option.config.report-processing-in-process` | boolean | `true` | When `true`, report export and upload run in-process on background threads; when `false` they go through Android's `JobScheduler` so they survive process death, at the cost of upload latency. | -| `ReportHandlerCallbackTimeout` | `com.bugsee.option.config.report-handler-callback-timeout` | int (seconds) | `30` | How long to wait for a `ReportHandler` completion callback before auto-firing it so the report isn't lost. `0` disables the per-handler timeout; since 7.3.0 the whole handler chain for one callback is still capped at 60 s (or at this timeout, if longer). Not applied to terminating (crash) sequences. | +| `ReportHandlerCallbackTimeout` | `com.bugsee.option.config.report-handler-callback-timeout` | int (seconds) | `30` | How long to wait for a `ReportHandler` completion callback before auto-firing it so the report isn't lost. `0` disables the timeout; since 7.3.0 a handler that never completes still can't hold up its report indefinitely. Not applied to terminating (crash) sequences. | | `NotifyFlushDelay` | `com.bugsee.option.config.notify-flush-delay` | int (ms) | `0` | Coalescing window before the [notification relay](/sdk/android/notification-relay) queue drains; `0` starts the drain as soon as a notification is persisted. Only affects the non-urgent, batched drain — not an urgent `notify()` skip-ahead POST. | -| `MaxDataSize` | `com.bugsee.option.config.max-data-size` | int (MB) | `150` | Maximum disk space that reports waiting to be sent may occupy. When exceeded, whole pending reports are deleted — errors first, then bug reports, then crashes, oldest first within each — until they fit. The newest crash and any report being exported or uploaded are never deleted. Values below `10` are treated as `10`. *(7.3.0)* | -| `MaxPendingReports` | `com.bugsee.option.config.max-pending-reports` | int | `30` | Maximum number of reports waiting to be sent; over it, pending reports are deleted in the same order as for `MaxDataSize`. Values below `1` are treated as `1`. *(7.3.0)* | -| `MaxPendingReportAge` | `com.bugsee.option.config.max-pending-report-age` | int (days) | `30` | Maximum age of a report waiting to be sent; older reports are deleted whatever their type. `0` or a negative value turns aging off. *(7.3.0)* | +| `MaxDataSize` | `com.bugsee.option.config.max-data-size` | int (MB) | `150` | Maximum disk space for reports waiting to be sent. Past it, the oldest reports are deleted, errors before bug reports and bug reports before crashes; the newest crash and reports being sent are always kept. Minimum `10`. *(7.3.0)* | +| `MaxPendingReports` | `com.bugsee.option.config.max-pending-reports` | int | `30` | Maximum number of reports waiting to be sent; past it, reports are deleted in the same order as for `MaxDataSize`. Minimum `1`. *(7.3.0)* | +| `MaxPendingReportAge` | `com.bugsee.option.config.max-pending-report-age` | int (days) | `30` | Maximum age of a report waiting to be sent; older reports are deleted. `0` or less turns aging off. *(7.3.0)* | -`Duration` caps the cyclical capture buffer (video, logs, breadcrumbs) at the given number of seconds. `WifiOnlyUpload` defers report uploads until Wi-Fi. `ReportProcessingInProcess = false` routes report processing through `JobScheduler` so it survives process death (at the cost of upload latency). `ReportHandlerCallbackTimeout = 0` disables the per-handler timeout (the 60 s chain cap still applies since 7.3.0) — and the timeout doesn't apply to crash sequences. `NotifyFlushDelay` delays the notification-relay drain so nearby `Bugsee.notify(...)` calls can batch together. +`Duration` caps the cyclical capture buffer (video, logs, breadcrumbs) at the given number of seconds. `WifiOnlyUpload` defers report uploads until Wi-Fi. `ReportProcessingInProcess = false` routes report processing through `JobScheduler` so it survives process death (at the cost of upload latency). `ReportHandlerCallbackTimeout = 0` disables the timeout — but it doesn't apply to crash sequences. `NotifyFlushDelay` delays the notification-relay drain so nearby `Bugsee.notify(...)` calls can batch together. @@ -83,7 +83,7 @@ The full set of detection options is documented on each detector's own page in [ | `DetectFrustration` | `com.bugsee.option.detect.frustration` | boolean | `false` | Detect user-frustration patterns (rage taps, dead taps, stuck loading, frozen UI) and report them as simulated errors grouped by screen and element. | | `DetectAndReportExit` | `com.bugsee.option.detect.exit` | boolean | `true` | Detect and report application exits. The per-reason flags below select which exit reasons are reported. | | `DetectAndReportExitLowMemory` | `com.bugsee.option.detect.exit.low_memory` | boolean | `true` | Report exits where the OS killed the app to reclaim memory. | -| `DetectAndReportExitLowMemoryBackgroundAsError` | `com.bugsee.option.detect.exit.bg_low_memory_as_error` | boolean | `false` | Report a low-memory kill of a process that was not in the foreground (importance foreground or foreground service) as a non-fatal error instead of a crash. Foreground kills stay crashes. Applies only on devices that report low-memory kills; elsewhere a background kill is already a non-fatal error. Requires `DetectAndReportExitLowMemory`. *(7.3.0)* | +| `DetectAndReportExitLowMemoryBackgroundAsError` | `com.bugsee.option.detect.exit.bg_low_memory_as_error` | boolean | `false` | Report a low-memory kill of the app while it was not in the foreground as a non-fatal error instead of a crash. Foreground kills stay crashes. Requires `DetectAndReportExitLowMemory`. *(7.3.0)* | | `DetectAndReportExitNotResponding` | `com.bugsee.option.detect.exit.not_responding` | boolean | `true` | Report exits caused by the app not responding (ANR). | | `DetectAndReportExitNotRespondingAsCrash` | `com.bugsee.option.detect.exit.not_responding.as_crash` | boolean | `false` | Report ANR exits as crashes rather than as exits, so they group with crashes in the dashboard. Requires `DetectAndReportExitNotResponding`. *(7.1.0)* | | `DetectAndReportAnrSampling` | `com.bugsee.option.detect.anr.sampling` | boolean | `true` | Sample the main thread's stack during an ANR and attribute the report to the derived culprit frames rather than to a single snapshot taken once the OS confirms the ANR. Only has effect when `DetectAndReportExitNotResponding` is enabled. *(7.2.0)* | @@ -268,7 +268,7 @@ val opts: OptionsContainer = Bugsee.getLaunchOptions() val duration: Int = opts.getOption(Options.Duration, 60) ``` -`Bugsee.getHostLaunchOptions()` *(7.3.0)* returns only the options your app supplied — through `Bugsee.launch(...)` or manifest `` — without the SDK defaults merged in, as an unmodifiable `Map`. Only option keys the SDK recognizes are included, with the values it accepted. The map is replaced, not merged, by the next `launch()` or `relaunch()`. +`Bugsee.getHostLaunchOptions()` *(7.3.0)* returns only the options your app supplied, through `Bugsee.launch(...)` or manifest ``, as a read-only `Map` without the SDK defaults. Unrecognized keys and rejected values are left out. Each `launch()` or `relaunch()` replaces it. ## Worked examples diff --git a/docs/sdk/android/issue-detection/exits.mdx b/docs/sdk/android/issue-detection/exits.mdx index b6c724b..37c4549 100644 --- a/docs/sdk/android/issue-detection/exits.mdx +++ b/docs/sdk/android/issue-detection/exits.mdx @@ -70,7 +70,7 @@ Bugsee.launch(this, "", options); | Other | `DetectAndReportExitOther` | `com.bugsee.option.detect.exit.other` | `false` | | Unknown | `DetectAndReportExitUnknown` | `com.bugsee.option.detect.exit.unknown` | `false` | -By default, low-memory kills are reported as crashes. Background low-memory kills are routine on modern Android, so counting them as crashes can inflate your crash rate; set `DetectAndReportExitLowMemoryBackgroundAsError` (`com.bugsee.option.detect.exit.bg_low_memory_as_error`, default `false`, new in 7.3.0) to report a kill of a process that was not in the foreground as a non-fatal error instead. Foreground kills stay crashes. The option applies only on devices that report low-memory kills; elsewhere a background kill is already reported as a non-fatal error. +By default, low-memory kills are reported as crashes. To report a kill while the app was not in the foreground as a non-fatal error instead, set `DetectAndReportExitLowMemoryBackgroundAsError` (`com.bugsee.option.detect.exit.bg_low_memory_as_error`, default `false`, new in 7.3.0). Foreground kills stay crashes. The reasons map directly onto `ApplicationExitInfo.getReason()` codes — see [the platform reference](https://developer.android.com/reference/android/app/ApplicationExitInfo) for the upstream documentation. diff --git a/docs/sdk/android/release-notes.md b/docs/sdk/android/release-notes.md index bf74424..3e834a8 100644 --- a/docs/sdk/android/release-notes.md +++ b/docs/sdk/android/release-notes.md @@ -9,56 +9,44 @@ Release history for Bugsee Android SDK 7.x. Looking for the previous major versi ## 7.3.0 -A security and reliability release. It stops the SDK's own diagnostic log from carrying your app's -request URLs and user values into reports. Reports now say which of their parts is missing and why, -instead of leaving it out silently. Several causes of broken report video are fixed. New options -limit how much unsent report data the SDK keeps on the device. +A security and reliability release. Reports no longer carry your app's request URLs or user values +in the SDK's internal log, and they now say which of their parts is missing and why. Report video +is more reliable, and new options limit how much unsent report data stays on the device. **Security** - **The SDK's internal log no longer records your app's URLs or user values.** In 7.0.0-beta1 - through 7.2.0, the SDK's own diagnostic log (the "Internal log" attached to every report) could - include your app's HTTP request URLs, with any embedded credentials (`user:password@`) and full - query strings. They came from the HttpURLConnection, OkHttp, Ktor and WebView failure paths, and - from raw logcat lines of your app that the SDK could not parse. The same log also recorded: - - the values passed to `Bugsee.setUserIdentifier()` and `Bugsee.setAttribute()`; - - the names of `http.client` performance transactions, which contain URLs; - - the `toString()` output and exception messages of filters and listeners your app registers. - - These log lines now carry no data from your app, only class names, ids and counts. As a second - layer, every internal-log record is sanitized when it is written: credentials are stripped from - URLs and sensitive query values are redacted. Records written by an earlier SDK build are deleted - on the first launch after the upgrade and are never attached to a report; anything that cannot - be deleted is left out of reports. - - The data went only into the reports uploaded to your own Bugsee project; no third party received - it. The 6.x SDK is not affected. We recommend upgrading any app on an affected 7.x version. + through 7.2.0, the "Internal log" attached to every report could include: + - your app's HTTP request URLs, with any embedded credentials (`user:password@`) and full query + strings; + - values passed to `Bugsee.setUserIdentifier()` and `Bugsee.setAttribute()`; + - URLs in performance transaction names; + - the text and error messages of filters and listeners your app registers. + + 7.3.0 keeps this data out of the internal log, and internal-log data written by an earlier + version is deleted on the first launch after the upgrade and never attached to a report. The data + went only to your own Bugsee project; no third party received it. The 6.x SDK is not affected. + We recommend upgrading any app on 7.0.0-beta1 through 7.2.0. :::note[Known limits] - - Records written by an older build *after* 7.3.0 has run (a downgrade followed by another - upgrade) are not deleted. - - Reports that were already bundled before the upgrade and are still waiting to upload are sent - as they are. - - With the `$$DEBUG` option on, log lines are echoed to logcat before they are sanitized. + - If you downgrade after running 7.3.0 and then upgrade again, the internal-log data the older + version wrote in between is not deleted. + - Reports already waiting to upload when you upgrade are sent as they are. + - With the `$$DEBUG` option on, SDK log lines appear in logcat unredacted. - Option values the SDK rejects at launch are still logged as given. - - **React Native:** native code that uses HttpURLConnection, and the fallback path of - `react-native-webview`, were affected as described above. JavaScript `fetch` and - `XMLHttpRequest` run on OkHttp and are captured only when `bugsee-android-okhttp` is in the - app; there the exposure was at most a host name, and only when Bugsee's own event handling - failed. + - **React Native:** apps are affected through native code that uses HttpURLConnection and, in + some cases, through `react-native-webview`. JavaScript `fetch` and `XMLHttpRequest` exposed at + most a host name, and only when `bugsee-android-okhttp` is in the app. ::: **New features** -- **Reports say what is missing, and why.** When a part of a report could not be delivered — the - video, a screenshot, a capture stream or the crash details — the report now lists it as absent, - with a reason, instead of leaving it out silently. In this release that happens when a file is - lost while the report is being bundled (reason `export_failed`), and when a crash or error report - has no crash details at all (see the crash-report fix below). Attachments are never listed as - absent. The Bugsee servers already accept the new form. +- **Reports say what is missing, and why.** When a report's video, a screenshot, a capture stream + or its crash details could not be included, the report now lists that part as absent with a + reason, instead of leaving it out silently. Attachments are never listed as absent. -- **Limits on reports waiting to be sent.** Reports that have not been uploaded yet used to - accumulate on the device without bound. Three new options cap them: +- **Limits on reports waiting to be sent.** Three new options cap how much unsent report data the + SDK keeps on the device: | `Options` constant | Manifest key | Default | | --- | --- | --- | @@ -66,261 +54,162 @@ limit how much unsent report data the SDK keeps on the device. | `MaxPendingReports` | `com.bugsee.option.config.max-pending-reports` | `30` (minimum `1`) | | `MaxPendingReportAge` | `com.bugsee.option.config.max-pending-report-age` | `30` (days; `0` or less turns aging off) | - Reports older than the age limit are deleted whatever their type. Over the size or count limit, - whole reports are deleted — errors first, then bug reports, then crashes, oldest first within - each — until the rest fit. The newest crash and any report being exported or uploaded are never - deleted. The check runs at launch, before each new report is exported, and when free disk space - runs low. + Past a limit, the oldest reports are deleted, errors before bug reports and bug reports before + crashes. The newest crash and reports being sent are always kept. :::note The limits are on by default. A device that stays offline for more than 30 days, or collects more than 30 unsent reports, now loses the oldest ones instead of keeping them all. ::: -- **Report background low-memory kills as errors.** Background low-memory kills are routine on - modern Android — cached, frozen or push-woken processes are reclaimed all the time — so counting - them as crashes can inflate your crash rate. Play Console's user-perceived low-memory metric - counts foreground kills only. The new `DetectAndReportExitLowMemoryBackgroundAsError` option - (`com.bugsee.option.detect.exit.bg_low_memory_as_error`, default `false`) reports a low-memory - kill of a process that was not in the foreground as a non-fatal error. A process counts as - foreground when its importance at death was foreground or foreground service. Foreground kills - are reported as crashes either way. The option applies only on devices that report low-memory - kills; on the others a background kill is already reported as a non-fatal error. +- **Report background low-memory kills as errors.** Set + `DetectAndReportExitLowMemoryBackgroundAsError` + (`com.bugsee.option.detect.exit.bg_low_memory_as_error`, default `false`) to report a low-memory + kill of your app while it was not in the foreground as a non-fatal error instead of a crash. + Foreground kills are still reported as crashes. On devices that don't report low-memory kills, + background kills are already reported as errors. - **A public network sanitizer for your network filter.** A network filter replaces the built-in - redaction of network events. To keep that redaction inside your filter, call - `Bugsee.getDefaultNetworkSanitizer().sanitize(event)`. The method returns a stateless - `NetworkDataSanitizer` (package `com.bugsee.library.contracts.exchange`) that is safe from any - thread and works before `Bugsee.launch()`, so you can also use it in a manifest-declared filter. - It also has `sanitizeUrl(event)` and `sanitizeErrorMessage(event)`. See + redaction; to keep it, call `Bugsee.getDefaultNetworkSanitizer().sanitize(event)` in your + filter. See [Privacy → Network traffic](/sdk/android/privacy/network#a-filter-replaces-the-built-in-redaction). - The static `com.bugsee.library.shared.security.privacy.NetworkDataSanitizer` class is deprecated - but keeps working. + The static `com.bugsee.library.shared.security.privacy.NetworkDataSanitizer` is deprecated but + keeps working. - **See the options your app passed at launch.** `Bugsee.getHostLaunchOptions()` returns the - options your app supplied to `Bugsee.launch()` or in manifest ``, without the SDK - defaults merged in, so you can tell "the app asked for this" apart from "this is the default". - Only option keys the SDK recognizes are included, with the values it accepted. The map is - replaced, not merged, by the next `launch()` or `relaunch()`. `Bugsee.getLaunchOptions()` still - returns the options in effect. - -- **Attach files without loading them into memory.** In a report handler, - `Report.addAttachment(File file, String name, String mimeType, boolean move)` moves or copies a - file into the report, so large logs and database dumps are fine. It returns `null` and leaves the - file where it is when it cannot be added. `Report.addAttachment(byte[] data, String name, String - mimeType)` adds small in-memory content. A report now refuses new attachments, including through - `createAndAddAttachment()`, once its handler has completed or it has been submitted, since they - would never reach the upload. - -- **Annotated screenshots are marked as such.** A screenshot is now marked annotated only when the - user drew on it in the screenshot editor, as on iOS. Before, Android sent no annotation flag, so - every Android screenshot was treated as annotated and shown over the video. Your own code can - set the flag with `Report.setScreenshot(displayId, bitmap, annotated)` and read it with - `Report.isScreenshotAnnotated(displayId)`. `Report.getScreenshotDisplayIds()` now returns the - display ids in ascending order. - -- **Fold state on foldable devices.** On foldable devices, reports now carry `fold_state` and - `hinge_angle` system traces: the posture (closed, partially open or fully open), the bounds of - each fold, and the hinge angle (API 30 and newer). No new dependency is needed; if your app - already bundles `androidx.window` 1.1 or newer, the SDK can use it. - -- **Stylus tilt and direction.** Stylus and eraser touches now record the pen's tilt and the - direction it points, in the same form the iOS SDK uses, when the device reports those axes. + options your app supplied to `Bugsee.launch()` or in the manifest, without the SDK defaults. + `Bugsee.getLaunchOptions()` still returns the options in effect. + +- **Attach large files to a report.** In a report handler, `Report.addAttachment(File file, String + name, String mimeType, boolean move)` moves or copies a file into the report without loading it + into memory, and `Report.addAttachment(byte[] data, String name, String mimeType)` adds small + in-memory content. Both return `null` if the attachment could not be added, including after the + report has been submitted. + +- **Annotated screenshots are marked as such.** Only screenshots the user drew on are now shown as + annotations on the issue; before, every Android screenshot was. Use + `Report.setScreenshot(displayId, bitmap, annotated)` and `Report.isScreenshotAnnotated(displayId)` + to set and read the flag yourself. + +- **Fold state on foldable devices.** Reports from foldable devices now include the fold posture + and hinge angle as system traces. + +- **Stylus tilt and direction.** Stylus touches now record the pen's tilt and direction, as on + iOS. **Fixes** -- **Report videos no longer show green, stale or blended frames.** With software video encoders - on API 29 and newer, the encoder could read a frame before the SDK's copy of it had fully - arrived, and encoded empty memory (green), an earlier frame, or a mix of the two. In testing - this affected about one frame in five on an API 30 emulator. The SDK now feeds those encoders - through an input surface, and falls back to the previous method if that fails. Separately, - reports recovered right after an early crash could come out entirely green, because the video - code they needed was not loaded yet; it is now loaded before any export. A failed encoding pass - is now retried instead of shipping the report without video. - -- **Video no longer garbles or freezes at capture boundaries.** Each captured frame is stored as a - change against the frame before it. At a boundary between capture segments, a frame could be - stored against a base that sat in the previous segment, or be matched with the wrong base, and - the exported video showed garbled or frozen frames. A frame that opens a segment is now always a - full frame, frame times always increase, and export skips a frame whose base is missing instead - of decoding it into garbage. - -- **Frames right after a rotation are drawn in the new orientation.** For up to a few seconds after - the device rotated, video frames could show the old orientation's window cropped into the new - one, because the app had not yet laid its windows out again. The capture now holds the last frame - until the windows catch up, as the screen itself does during a rotation, for at most 5 seconds - per rotation. This applies to the default PixelCopy capture (`VideoMode.V2`). - -- **Touches line up with the video from the first frame.** A recording whose frames never needed - padding carried no video geometry at all, so the viewer had no screen size to place touches - against. Geometry is now recorded on the first frame and on every rotation. - -- **One stuck report no longer blocks every other report.** Reports are exported one at a time. - On Android 6, a video whose frame times went backwards could deadlock the encoder, and no report - was delivered again on any later launch. Frame times now always increase, a watchdog abandons a - stuck video so the report ships without it, and a report that keeps failing is exported without - video after three attempts and deleted after two more. Pending reports are now processed oldest - first. - -- **Crash reports always account for their crash details.** If the SDK cannot write a report's - crash details (for example under the memory pressure an `OutOfMemoryError` leaves behind), it - retries once with a minimal version: the exception class, message, top 32 frames and signature, - without the thread dump or cause chain. If that fails too, the report lists the crash details as - absent (`export_failed`). A crash or error report that reaches upload with no crash details at - all — for example, one recovered after the process died before they were written — lists them - as absent with reason `unknown`. The SDK now also saves the report as soon as its crash details - are registered, which narrows that window. `Bugsee.logException()` and `Bugsee.sendBugReport()` - now do this disk work off the main thread. +- **Report videos no longer show green, stale or blended frames.** This affected some devices, + and reports sent right after an early crash. + +- **Report videos no longer show garbled or frozen frames.** + +- **Video right after a rotation is no longer drawn in the old orientation.** + +- **Touches line up with the video from the start of the recording.** + +- **One report that failed to export no longer stops all other reports from being sent.** A report + that keeps failing is sent without its video, and eventually deleted. + +- **Crash reports always account for their crash details.** When the crash details cannot be + saved, the report now includes a shorter version of them or lists them as absent, instead of + arriving without them. `Bugsee.logException()` and `Bugsee.sendBugReport()` no longer do disk + work on the calling thread. :::note[Behavior change] - `Bugsee.logException(null)` now reports a simulated exception, built on the thread that called - it. On the dashboard these reports now group by call site, instead of all landing in one + `Bugsee.logException(null)` now reports a simulated exception from the calling code. On the + dashboard these reports now group by call site, instead of all landing in one `` issue. ::: -- **Report handlers no longer run on the main thread.** A hang or an ANR *is* a blocked main - thread, so handlers dispatched there could not run for exactly the reports where they matter - most. Handlers for a healthy report now run on a dedicated SDK background thread that has a - `Looper`, so `new Handler()` and `Toast` still work there. The thread is started only when a - report actually has a handler to run. The wrapper and app handlers for one callback are now - capped together at 60 seconds (or one `ReportHandlerCallbackTimeout`, if that is longer), so a - `ReportHandlerCallbackTimeout` of `0` no longer lets a handler hold a report forever. During - early-crash recovery at launch, handlers are now told `isTerminating = false`, because that - process is alive. +- **Report handlers no longer run on the main thread,** so they also run for hang and ANR reports. + With `ReportHandlerCallbackTimeout` set to `0`, a handler that never completes no longer holds up + its report indefinitely. :::caution[Behavior change] If your `ReportHandler` touches views or other UI state, post that work to the main thread - yourself, for example with `new Handler(Looper.getMainLooper()).post(...)`. On the crash paths - the SDK still prefers a background thread, but when it cannot get one the handler runs on the - thread finishing the report, which during early-crash recovery can be the main thread; keep - handlers short there. + yourself, for example with `new Handler(Looper.getMainLooper()).post(...)`. While a crash is + being handled, a handler can still occasionally run on the main thread, so keep it short. ::: -- **Recovered crashes are reported against the build that crashed.** Native crashes and exits the - system reports (ANRs, low-memory kills and the like) are turned into reports on the next launch. - After an app update, they were attributed to the new version. Each capture session now records - its app and SDK versions, and a recovered report uses those. A race at launch could also delete - the dead session's capture data, so some recovered reports carried no version at all and were - rejected by the server. Recovered reports now keep that session's capture, and always carry a - version; when none was recorded, they use the running app's version. - -- **A second crash on another thread no longer loses the first.** If another thread crashed while - the SDK was still saving a crash, the process was killed mid-save and the first report was lost — - common under memory pressure, where secondary thread deaths follow an `OutOfMemoryError`. The - second crash now waits, for at most 15 seconds, until the first is saved. - -- **Network events are no longer dropped after a clock change.** If the device clock was corrected - while the app was running, as on a phone that boots offline and gets network time later, every - network event from OkHttp, Ktor, Cronet and the Android 14 `HttpEngine` was silently dropped for - the rest of the process. These events now use the SDK's own clock. If you create network events - yourself with `Bugsee.getExchangeFactory()`, use the new - `BugseeExchangeFactory.currentTimestamp()` for their timestamps. - -- **Breadcrumbs and long transactions are no longer dropped.** A breadcrumb passed to - `Bugsee.addBreadcrumb()` as your own `Breadcrumb` implementation lost its timestamp and was - discarded, and a breadcrumb that a breadcrumb filter returned after a delay could be discarded - too. A performance transaction that outlasted the current one-second capture segment was left - out of report capture as well. - -- **Report delivery is more reliable.** - - A report whose capture data could not be exported, for example on a full disk, was deleted with - its crash details, screenshot and attachments. It is now sent with what it has. - - When two processes of the same app exported the same report at once, the uploaded report could - name one attempt's files while containing the other's, and the server then dropped all of its - capture data. One process now publishes each report. - - A report is bundled from exactly the files it lists, so a retried export no longer uploads the - failed attempt's video and data as well. - - The same pending report is no longer exported twice at launch. - - A report could contain half-written capture entries, which cost video frames and performance - data. It now copies only what the SDK finished writing. - - Starting capture with a large backlog of pending reports could run out of memory on devices - with a small heap. - - An empty or unreadable capture segment no longer corrupts a whole exported log or event - stream. - -- **Rare crashes and hangs caused by the SDK are fixed.** - - A race while screen capture was stopping or relaunching could crash the app with a - `NullPointerException` on a Bugsee capture thread, or leave the session recording black video - until the next restart. - - Two report exports running at the same time could crash the app with a - `ConcurrentModificationException`. - - On a device whose Android 16 framework lacks the satellite API, capture start crashed the app - with a `NoClassDefFoundError`. An SDK trace that fails to start is now left out instead. - - An exception in an SDK background task could reach the default handler and kill the app, or - silently stop a periodic job (the APM upload, the frozen-UI and stuck-loading detectors) for - the rest of the session. - - If loading the SDK's preferences failed or stalled, the app could crash, or the main thread - could wait long enough for an ANR. - - Stopping the SDK while the first frame was being drawn could crash the main thread. - -- **The Ktor extensions compile again.** In the published 7.2.0 `bugsee-android-ktor-2` and +- **Native crashes and system-reported exits are attributed to the app version that crashed,** not + to the version installed when they were sent. Some of these reports were previously not + delivered at all; they now are. + +- **A crash on a second thread no longer causes the first crash's report to be lost.** + +- **Network events are no longer lost after the device clock changes.** If you create network + events yourself with `Bugsee.getExchangeFactory()`, stamp them with the new + `BugseeExchangeFactory.currentTimestamp()`. + +- **Breadcrumbs added as your own `Breadcrumb` objects, and breadcrumbs returned late by a + breadcrumb filter, are no longer lost.** Long performance transactions are no longer missing + from reports. + +- **Report delivery is more reliable.** Fixed: + - on a full disk, a report was deleted along with its crash details, screenshot and attachments; + it is now sent with what it has; + - in apps with several processes, a report could arrive with all of its capture data missing; + - a retried report could upload extra data; + - reports could be missing video frames or performance data; + - with many unsent reports, apps on low-memory devices could run out of memory at launch; + - a whole log or event stream could be missing from a report. + +- **Rare crashes and hangs caused by the SDK are fixed.** Fixed: + - crashes when stopping or relaunching the SDK; + - a crash when two reports were exported at the same time; + - a crash at launch on some Android 16 devices; + - crashes from SDK background work, and background work that stopped for the rest of the + session; + - a crash or ANR when the SDK's saved settings could not be read; + - black video after the SDK was stopped and started again in quick succession. + +- **The Ktor extensions compile again.** With the 7.2.0 `bugsee-android-ktor-2` and `bugsee-android-ktor-3` artifacts, `install(BugseeKtor2Plugin)`, `install(BugseeKtor3Plugin.Plugin)` - and the `bugseeWebSocket` helpers did not compile in Kotlin apps, because code shrinking had - renamed or removed them. + and the `bugseeWebSocket` helpers did not compile in Kotlin apps. -- **Enum options can be passed by name in the launch map.** An enum option passed to - `Bugsee.launch(context, token, options)` as a string naming the constant (for example `"High"` - for `VideoQuality`) was accepted but never converted, so the option could fail when the SDK read - it. Names are now matched case-insensitively, the same as in the manifest. An integer is still - not accepted in place of an enum constant. +- **Enum options can be passed by name in the launch map,** for example `"High"` for + `VideoQuality`, matched case-insensitively as in the manifest. Before, such a value was accepted + but did not work. -- **Leak detection no longer leaks.** The `bugsee-android-leak` extension held on to the views of - destroyed fragments — between 2.5 and 21 MB in testing. Fragments destroyed together with their - activity were never watched for leaks at all; they are now. +- **The leak detection extension no longer keeps destroyed fragments' views in memory,** and now + also watches fragments that are destroyed together with their activity. -- **Anomaly issues no longer split by port.** An explicit port in a URL was part of the anomaly - grouping key, so a local or development server on a new port each run opened a new issue each - time. Stale endpoints could also fill the detector's baselines and leave live endpoints without - one; the least recently used ones are now evicted. +- **Anomaly issues no longer split by URL port,** and new endpoints are no longer ignored after + many others have been seen. :::note - Existing anomaly issues whose URL had an explicit port regroup once under the port-free key. + Existing anomaly issues whose URL had an explicit port regroup once. ::: -- **HTTP-error reports for different endpoints no longer suppress each other.** Their - de-duplication key could collide, so a report for one endpoint silenced another's. +- **HTTP-error reports for one endpoint no longer suppress reports for another.** -- **Filters that misbehave are easier to spot.** A log or network filter that never calls its - callback now logs one warning per process (the events it holds are still dropped). A filter that - calls its callback and then throws no longer corrupts the event it returned. +- **A log or network filter that never calls its callback now logs a warning.** A filter that + throws after calling its callback no longer corrupts the event. **Privacy** -- **Filters set before launch apply to data captured at launch.** A log, network or breadcrumb - filter set before `Bugsee.launch()`, or declared in the manifest, was not yet in force for the - data captured while launch was starting up, so that data was recorded unfiltered. Manifest - filters are now installed before capture starts. - -- **Removing the only screenshot in the bug-report dialog removes it from the report.** It was - hidden in the dialog but still uploaded, and, if the user had drawn on it, shown on the issue as - annotated. - -- **Secure fields are no longer named in breadcrumbs, detections or the view hierarchy.** A secure - field's pixels, touches and keys were withheld, but its identity was not: its id and tag reached - breadcrumbs (for example `ui.click` and `focus.set`), its id reached rage-tap reports, and its - resource name reached the view-hierarchy dump. Breadcrumbs now leave out the id and tag and carry - `view.secure: true`, rage-tap reports show `` instead, and the dump leaves out the - resource name. The view class is kept. - -- **URL credentials are stripped wherever the URL is reported.** The built-in network redaction now - also removes `user:password@` from the captured request URL and from error messages, and the - performance transaction names and `http.url` attributes drop them too. HTTP-error reports strip - them whether or not a network filter is installed; before, only with one. - -- **A truncated body is no longer recorded unredacted.** A JSON body too large to capture in full - could be recorded cut short, and cut-off JSON cannot be parsed for redaction, so it was recorded - as it was. Such a body is now left out and marked as too large. The built-in redaction also - covers network error messages now. - -- **More fields are recognized as secure.** The built-in rule for Stripe, Braintree and card.io - payment widgets now reaches their input fields, not only their containers. View-based fields - with the `creditCardExpirationDay` or `newPassword` autofill hint are now secure, as they already - were in Compose. Keys such as `KEYCODE_RO` and `KEYCODE_YEN`, which type characters on Brazilian - and Japanese keyboards, are now redacted like other character keys. - -- **A warning when Compose masking is missing.** Compose password fields are masked only by the - `bugsee-android-compose` extension. The SDK now logs a warning once per process when it sees - Compose UI on screen and that extension is not in the app. +- **Filters set before `Bugsee.launch()`, or declared in the manifest, now also apply to data + captured during launch.** + +- **Removing the only screenshot in the bug-report dialog now removes it from the report.** + +- **Secure fields are no longer identified by their id or tag** in breadcrumbs, rage-tap reports + or the view hierarchy. Breadcrumbs for them carry `view.secure: true`. + +- **Credentials in URLs (`user:password@`) are now removed.** The built-in redaction removes them + from captured network URLs and error messages, and performance transactions and HTTP-error + reports always drop them. + +- **Network bodies too large to capture are no longer recorded partially and unredacted,** and + network error messages are now redacted too. + +- **More fields are treated as secure:** input fields in Stripe, Braintree and card.io payment + views, views with the `creditCardExpirationDay` or `newPassword` autofill hint, and character + keys specific to Brazilian and Japanese keyboards. + +- **A warning when Compose masking is missing.** The SDK logs a warning if your app shows Compose + UI without the `bugsee-android-compose` extension, which is what masks Compose password fields. **Compatibility** From 384863dea41f6ad51be8b45a694855be8034cbb7 Mon Sep 17 00:00:00 2001 From: Alexey Karimov Date: Tue, 29 Sep 2026 18:20:48 +0500 Subject: [PATCH 4/4] docs(android): claim absent entries only for what 7.3.0 lists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Change-Id: Ib70f3be8fc7bc3aa2483fd160170b858433f90b6 --- docs/sdk/android/release-notes.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/sdk/android/release-notes.md b/docs/sdk/android/release-notes.md index 3e834a8..e71cfde 100644 --- a/docs/sdk/android/release-notes.md +++ b/docs/sdk/android/release-notes.md @@ -41,9 +41,10 @@ is more reliable, and new options limit how much unsent report data stays on the **New features** -- **Reports say what is missing, and why.** When a report's video, a screenshot, a capture stream - or its crash details could not be included, the report now lists that part as absent with a - reason, instead of leaving it out silently. Attachments are never listed as absent. +- **Reports say what is missing, and why.** When a file that belongs to a report goes missing + before the report is sent, or a crash or error report has no crash details, the report now lists + that part as missing, with a reason, instead of leaving it out silently. Attachments are never + listed as missing. - **Limits on reports waiting to be sent.** Three new options cap how much unsent report data the SDK keeps on the device: