diff options
| author | 2022-08-12 18:06:23 +0200 | |
|---|---|---|
| committer | 2022-08-30 09:16:54 +0000 | |
| commit | 166d420cf4d93a7f01d8cd2e9dc5d14446dfdc4b (patch) | |
| tree | ef360cfec269dfdc364d49e5b4216174b90002d3 | |
| parent | 7b43124412cdc886e154f33583a89e683bd73ae3 (diff) | |
Prevent loading of Icon resources from the wrong package
If an Icon carries package designator, LocalImageResolver has now
ignored it. This could lead to loading of a wrong icon in cases where
the ID collided with the same ID in android package.
This is fixes that corner-case.
Bug: 241066484
Test: atest LocalImageResolverTest - the new test checks for pkg
correctness and fails on devices without this cl
manual test: bluejay device with known, reproducible resource
collision in media player notification. In reproducible case,
icon appears as a broken block.
After applying this patch, small icon in media notification loads
correctly for the collision case.
Change-Id: Ic79c0d4acb1d347a24282972e009a5a079ce6c21
| -rw-r--r-- | core/java/com/android/internal/widget/LocalImageResolver.java | 65 | ||||
| -rw-r--r-- | core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java | 47 |
2 files changed, 108 insertions, 4 deletions
diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java index b11ea2961c17..9ef7ce38fc09 100644 --- a/core/java/com/android/internal/widget/LocalImageResolver.java +++ b/core/java/com/android/internal/widget/LocalImageResolver.java @@ -19,6 +19,8 @@ package com.android.internal.widget; import android.annotation.DrawableRes; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.ImageDecoder; @@ -109,13 +111,13 @@ public class LocalImageResolver { } break; case Icon.TYPE_RESOURCE: - if (!(TextUtils.isEmpty(icon.getResPackage()) - || context.getPackageName().equals(icon.getResPackage()))) { - // We can't properly resolve icons from other packages here, so fall back. + Resources res = resolveResourcesForIcon(context, icon); + if (res == null) { + // We couldn't resolve resources properly, fall back to icon loading. return icon.loadDrawable(context); } - Drawable result = resolveImage(icon.getResId(), context, maxWidth, maxHeight); + Drawable result = resolveImage(res, icon.getResId(), maxWidth, maxHeight); if (result != null) { return tintDrawable(icon, result); } @@ -159,6 +161,13 @@ public class LocalImageResolver { } @Nullable + private static Drawable resolveImage(Resources res, @DrawableRes int resId, int maxWidth, + int maxHeight) { + final ImageDecoder.Source source = ImageDecoder.createSource(res, resId); + return resolveImage(source, maxWidth, maxHeight); + } + + @Nullable private static Drawable resolveBitmapImage(Icon icon, Context context, int maxWidth, int maxHeight) { @@ -259,4 +268,52 @@ public class LocalImageResolver { } return icon.getUri(); } + + /** + * Resolves the correct resources package for a given Icon - it may come from another + * package. + * + * @see Icon#loadDrawableInner(Context) + * @hide + * + * @return resources instance if the operation succeeded, null otherwise + */ + @Nullable + @VisibleForTesting + public static Resources resolveResourcesForIcon(Context context, Icon icon) { + if (icon.getType() != Icon.TYPE_RESOURCE) { + return null; + } + + // Icons cache resolved resources, use cache if available. + Resources res = icon.getResources(); + if (res != null) { + return res; + } + + String resPackage = icon.getResPackage(); + // No package means we try to use current context. + if (TextUtils.isEmpty(resPackage) || context.getPackageName().equals(resPackage)) { + return context.getResources(); + } + + if ("android".equals(resPackage)) { + return Resources.getSystem(); + } + + final PackageManager pm = context.getPackageManager(); + try { + ApplicationInfo ai = pm.getApplicationInfo(resPackage, + PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.GET_SHARED_LIBRARY_FILES); + if (ai != null) { + return pm.getResourcesForApplication(ai); + } + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, String.format("Unable to resolve package %s for icon %s", resPackage, icon)); + return null; + } + + return null; + } } diff --git a/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java b/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java index 0cee526651a6..271a20b71106 100644 --- a/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java @@ -17,6 +17,8 @@ package com.android.internal.widget; import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.Resources; import android.graphics.BitmapFactory; import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.BitmapDrawable; @@ -279,4 +281,49 @@ public class LocalImageResolverTest { // This drawable must not be loaded - if it was, the code ignored the package specification. assertThat(d).isNull(); } + + @Test + public void resolveResourcesForIcon_notAResourceIcon_returnsNull() { + Icon icon = Icon.createWithContentUri(Uri.parse("some_uri")); + assertThat(LocalImageResolver.resolveResourcesForIcon(mContext, icon)).isNull(); + } + + @Test + public void resolveResourcesForIcon_localPackageIcon_returnsPackageResources() { + Icon icon = Icon.createWithResource(mContext, R.drawable.test32x24); + assertThat(LocalImageResolver.resolveResourcesForIcon(mContext, icon)) + .isSameInstanceAs(mContext.getResources()); + } + + @Test + public void resolveResourcesForIcon_iconWithoutPackageSpecificed_returnsPackageResources() { + Icon icon = Icon.createWithResource("", R.drawable.test32x24); + assertThat(LocalImageResolver.resolveResourcesForIcon(mContext, icon)) + .isSameInstanceAs(mContext.getResources()); + } + + @Test + public void resolveResourcesForIcon_systemPackageSpecified_returnsSystemPackage() { + Icon icon = Icon.createWithResource("android", R.drawable.test32x24); + assertThat(LocalImageResolver.resolveResourcesForIcon(mContext, icon)).isSameInstanceAs( + Resources.getSystem()); + } + + @Test + public void resolveResourcesForIcon_differentPackageSpecified_returnsPackageResources() throws + PackageManager.NameNotFoundException { + String pkg = "com.android.settings"; + Resources res = mContext.getPackageManager().getResourcesForApplication(pkg); + int resId = res.getIdentifier("ic_android", "drawable", pkg); + Icon icon = Icon.createWithResource(pkg, resId); + + assertThat(LocalImageResolver.resolveResourcesForIcon(mContext, icon).getDrawable(resId, + mContext.getTheme())).isNotNull(); + } + + @Test + public void resolveResourcesForIcon_invalidPackageSpecified_returnsNull() { + Icon icon = Icon.createWithResource("invalid.package", R.drawable.test32x24); + assertThat(LocalImageResolver.resolveResourcesForIcon(mContext, icon)).isNull(); + } } |