Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 166d420c authored by Jernej Virag's avatar Jernej Virag
Browse files

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
parent 7b431244
Loading
Loading
Loading
Loading
+61 −4
Original line number Diff line number Diff line
@@ -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);
                }
@@ -158,6 +160,13 @@ public class LocalImageResolver {
        return resolveImage(source, maxWidth, maxHeight);
    }

    @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;
    }
}
+47 −0
Original line number Diff line number Diff line
@@ -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();
    }
}