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
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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. */
|
||||
@@ -1071,7 +1076,7 @@ public class PeopleSpaceWidgetManager {
|
||||
return;
|
||||
}
|
||||
for (int appWidgetId : appWidgetIds) {
|
||||
if (DEBUG) Log.d(TAG, "Updating widget from broadcast, widget id: " + appWidgetId);
|
||||
if (DEBUG) Log.d(TAG, "Updating widget from broadcast, widget id: " + appWidgetId);
|
||||
PeopleSpaceTile existingTile = null;
|
||||
PeopleSpaceTile updatedTile = null;
|
||||
try {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user