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

Commit cdc15d70 authored by Anna Zappone's avatar Anna Zappone
Browse files

Prevent any SecurityExceptions on Uri resolution

By deferring resolution of the Uri to Launcher, we risk a
SecurityException. This is extremely important to avoid as if we the
SecurityException is thrown, we can no longer make any updates to the
widget.

Bug: 192986188
Test: locally with Signal
Change-Id: I5421ff5494b405ab9d48afd678fd72dc570a655b
parent 50c03ea7
Loading
Loading
Loading
Loading
+50 −4
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.ImageDecoder;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.graphics.text.LineBreaker;
@@ -59,6 +60,7 @@ import android.text.TextUtils;
import android.util.IconDrawableFactory;
import android.util.Log;
import android.util.Pair;
import android.util.Size;
import android.util.SizeF;
import android.util.TypedValue;
import android.view.Gravity;
@@ -79,6 +81,7 @@ import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
import com.android.systemui.people.widget.PeopleTileKey;

import java.io.IOException;
import java.text.NumberFormat;
import java.time.Duration;
import java.util.ArrayList;
@@ -677,15 +680,24 @@ public class PeopleTileViewHelper {
        RemoteViews views = setViewForContentLayout(new RemoteViews(mContext.getPackageName(),
                getLayoutForNotificationContent()));
        CharSequence sender = mTile.getNotificationSender();
        Uri image = mTile.getNotificationDataUri();
        if (image != null) {
            // TODO: Use NotificationInlineImageCache
            views.setImageViewUri(R.id.image, image);
        Uri imageUri = mTile.getNotificationDataUri();
        if (imageUri != null) {
            String newImageDescription = mContext.getString(
                    R.string.new_notification_image_content_description, mTile.getUserName());
            views.setContentDescription(R.id.image, newImageDescription);
            views.setViewVisibility(R.id.image, View.VISIBLE);
            views.setViewVisibility(R.id.text_content, View.GONE);
            try {
                Drawable drawable = resolveImage(imageUri, mContext);
                Bitmap bitmap = convertDrawableToBitmap(drawable);
                views.setImageViewBitmap(R.id.image, bitmap);
            } catch (IOException e) {
                Log.e(TAG, "Could not decode image: " + e);
                // If we couldn't load the image, show text that we have a new image.
                views.setTextViewText(R.id.text_content, newImageDescription);
                views.setViewVisibility(R.id.text_content, View.VISIBLE);
                views.setViewVisibility(R.id.image, View.GONE);
            }
        } else {
            setMaxLines(views, !TextUtils.isEmpty(sender));
            CharSequence content = mTile.getNotificationContent();
@@ -720,6 +732,40 @@ public class PeopleTileViewHelper {
        return views;
    }

    private Drawable resolveImage(Uri uri, Context context) throws IOException {
        final ImageDecoder.Source source =
                ImageDecoder.createSource(context.getContentResolver(), uri);
        final Drawable drawable =
                ImageDecoder.decodeDrawable(source, (decoder, info, s) -> {
                    onHeaderDecoded(decoder, info, s);
                });
        return drawable;
    }

    private static int getPowerOfTwoForSampleRatio(double ratio) {
        final int k = Integer.highestOneBit((int) Math.floor(ratio));
        return Math.max(1, k);
    }

    private void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info,
            ImageDecoder.Source source) {
        int widthInPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mWidth,
                mContext.getResources().getDisplayMetrics());
        int heightInPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mHeight,
                mContext.getResources().getDisplayMetrics());
        int maxIconSizeInPx = Math.max(widthInPx, heightInPx);
        int minDimen = (int) (1.5 * Math.min(widthInPx, heightInPx));
        if (minDimen < maxIconSizeInPx) {
            maxIconSizeInPx = minDimen;
        }
        final Size size = info.getSize();
        final int originalSize = Math.max(size.getHeight(), size.getWidth());
        final double ratio = (originalSize > maxIconSizeInPx)
                ? originalSize * 1f / maxIconSizeInPx
                : 1.0;
        decoder.setTargetSampleSize(getPowerOfTwoForSampleRatio(ratio));
    }

    private void setContentDescriptionForNotificationTextContent(RemoteViews views,
            CharSequence content, CharSequence sender) {
        String newTextDescriptionWithNotificationContent = mContext.getString(
+11 −6
Original line number Diff line number Diff line
@@ -375,7 +375,7 @@ public class PeopleSpaceWidgetManager {
                widgetSp.getInt(USER_ID, INVALID_USER_ID),
                widgetSp.getString(PACKAGE_NAME, EMPTY_STRING));

        return getTileFromPersistentStorage(key, appWidgetId);
        return getTileFromPersistentStorage(key, appWidgetId, /* supplementFromStorage= */ true);
    }

    /**
@@ -383,7 +383,8 @@ public class PeopleSpaceWidgetManager {
     * If a {@link PeopleTileKey} is not provided, fetch one from {@link SharedPreferences}.
     */
    @Nullable
    public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key, int appWidgetId) throws
    public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key, int appWidgetId,
            boolean supplementFromStorage) throws
            PackageManager.NameNotFoundException {
        if (!PeopleTileKey.isValid(key)) {
            Log.e(TAG, "PeopleTileKey invalid: " + key.toString());
@@ -412,7 +413,8 @@ public class PeopleSpaceWidgetManager {

            // Supplement with our storage.
            String contactUri = mSharedPrefs.getString(String.valueOf(appWidgetId), null);
            if (contactUri != null && storedTile.build().getContactUri() == null) {
            if (supplementFromStorage && contactUri != null
                    && storedTile.build().getContactUri() == null) {
                if (DEBUG) Log.d(TAG, "Restore contact uri from storage: " + contactUri);
                storedTile.setContactUri(Uri.parse(contactUri));
            }
@@ -811,7 +813,8 @@ public class PeopleSpaceWidgetManager {
        if (DEBUG) Log.d(TAG, "addNewWidget called with key for appWidgetId: " + appWidgetId);
        PeopleSpaceTile tile = null;
        try {
            tile = getTileFromPersistentStorage(key, appWidgetId);
            tile = getTileFromPersistentStorage(key, appWidgetId,  /* supplementFromStorage= */
                    false);
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Cannot add widget since app was uninstalled");
            return;
@@ -850,7 +853,9 @@ public class PeopleSpaceWidgetManager {
        } catch (Exception e) {
            Log.w(TAG, "Exception caching shortcut:" + e);
        }
        updateAppWidgetOptionsAndView(appWidgetId, tile);
        PeopleSpaceTile finalTile = tile;
        mBgExecutor.execute(
                () -> updateAppWidgetOptionsAndView(appWidgetId, finalTile));
    }

    /** Registers a conversation listener for {@code appWidgetId} if not already registered. */
+4 −2
Original line number Diff line number Diff line
@@ -1125,7 +1125,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
                new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A));
        when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(channel);
        PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A);
        PeopleSpaceTile tile = mManager.getTileFromPersistentStorage(key, WIDGET_ID_WITH_SHORTCUT);
        PeopleSpaceTile tile = mManager
                .getTileFromPersistentStorage(key, WIDGET_ID_WITH_SHORTCUT, true);
        assertThat(tile.getId()).isEqualTo(key.getShortcutId());
    }

@@ -1133,7 +1134,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
    public void testGetPeopleTileFromPersistentStorageNoConversation() throws Exception {
        when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(null);
        PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A);
        PeopleSpaceTile tile = mManager.getTileFromPersistentStorage(key, WIDGET_ID_WITH_SHORTCUT);
        PeopleSpaceTile tile = mManager
                .getTileFromPersistentStorage(key, WIDGET_ID_WITH_SHORTCUT, false);
        assertThat(tile).isNull();
    }