commit dcd39b58cd96d17bae87bc78d639277315baf982
parent eb7385b235a31cefe45f7cf5b5fdad33344c7e52
Author: mcarare <48995920+mcarare@users.noreply.github.com>
Date: Tue, 30 Sep 2025 19:47:35 +0000
Bug 1990183 - Prevent using recycled bitmaps for PWA icons. r=android-reviewers,boek
This patch avoids potential crashes caused by using recycled bitmaps when creating shortcuts for Progressive Web Apps (PWAs).
The changes ensure that:
- When creating an icon from the session's content, a check for `isRecycled` is added, and a copy of the bitmap is used to prevent it from being recycled prematurely.
- When building an icon from the web app manifest, it now checks if the fetched bitmap is recycled before use. It also creates a copy to prevent the bitmap from being recycled by the image cache after the shortcut is created.
Differential Revision: https://phabricator.services.mozilla.com/D266887
Diffstat:
1 file changed, 24 insertions(+), 7 deletions(-)
diff --git a/mobile/android/android-components/components/feature/pwa/src/main/java/mozilla/components/feature/pwa/WebAppShortcutManager.kt b/mobile/android/android-components/components/feature/pwa/src/main/java/mozilla/components/feature/pwa/WebAppShortcutManager.kt
@@ -146,8 +146,13 @@ class WebAppShortcutManager(
val icon = if (manifest != null && manifest.hasLargeIcons()) {
buildIconFromManifest(manifest)
} else {
- session.content.icon?.let { IconCompat.createWithBitmap(it) }
+ session.content.icon?.takeUnless { it.isRecycled }?.let {
+ val bitmapCopy =
+ it.copy(it.config ?: android.graphics.Bitmap.Config.ARGB_8888, false)
+ IconCompat.createWithBitmap(bitmapCopy)
+ }
}
+
icon?.let {
builder.setIcon(it)
}
@@ -172,22 +177,34 @@ class WebAppShortcutManager(
val shortLabel = manifest.shortName ?: manifest.name
storage.saveManifest(manifest)
- return ShortcutInfoCompat.Builder(context, manifest.startUrl)
+ val builder = ShortcutInfoCompat.Builder(context, manifest.startUrl)
.setLongLabel(manifest.name)
.setShortLabel(shortLabel.ifBlank { fallbackLabel })
- .setIcon(buildIconFromManifest(manifest))
.setIntent(shortcutIntent)
- .build()
+
+ buildIconFromManifest(manifest)?.let {
+ builder.setIcon(it)
+ }
+
+ return builder.build()
}
@VisibleForTesting
- internal suspend fun buildIconFromManifest(manifest: WebAppManifest): IconCompat {
+ internal suspend fun buildIconFromManifest(manifest: WebAppManifest): IconCompat? {
val request = manifest.toIconRequest()
val icon = icons.loadIcon(request).await()
+
+ if (icon.bitmap.isRecycled) {
+ return null
+ }
+
+ // Create a copy to prevent the icon from being recycled by the cache after we use it.
+ val bitmapCopy = icon.bitmap.copy(icon.bitmap.config ?: android.graphics.Bitmap.Config.ARGB_8888, false)
+
return if (icon.maskable) {
- IconCompat.createWithAdaptiveBitmap(icon.bitmap)
+ IconCompat.createWithAdaptiveBitmap(bitmapCopy)
} else {
- IconCompat.createWithBitmap(icon.bitmap)
+ IconCompat.createWithBitmap(bitmapCopy)
}
}