[DO NOT MERGE] Prevent RemoteViews crashing SystemUi
Catch canvas drawing exceptions caused by unsuported image sizes. Test: 1. Post a custom view notification with a layout containing an ImageView that references a 5k x 5k image 2. Add an App Widget to the home screen with that has the layout mentioned above as preview/initial layout. Bug: 268193777 Change-Id: Ib3bda769c499b4069b49c566b1b227f98f707a8a Merged-In: Ib3bda769c499b4069b49c566b1b227f98f707a8a
This commit is contained in:
@@ -31,6 +31,7 @@ import android.content.pm.LauncherActivityInfo;
|
|||||||
import android.content.pm.LauncherApps;
|
import android.content.pm.LauncherApps;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.PointF;
|
import android.graphics.PointF;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
@@ -312,19 +313,26 @@ public class AppWidgetHostView extends FrameLayout {
|
|||||||
super.onLayout(changed, left, top, right, bottom);
|
super.onLayout(changed, left, top, right, bottom);
|
||||||
} catch (final RuntimeException e) {
|
} catch (final RuntimeException e) {
|
||||||
Log.e(TAG, "Remote provider threw runtime exception, using error view instead.", e);
|
Log.e(TAG, "Remote provider threw runtime exception, using error view instead.", e);
|
||||||
removeViewInLayout(mView);
|
handleViewError();
|
||||||
View child = getErrorView();
|
|
||||||
prepareView(child);
|
|
||||||
addViewInLayout(child, 0, child.getLayoutParams());
|
|
||||||
measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
|
|
||||||
MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
|
|
||||||
child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight,
|
|
||||||
child.getMeasuredHeight() + mPaddingTop + mPaddingBottom);
|
|
||||||
mView = child;
|
|
||||||
mViewMode = VIEW_MODE_ERROR;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove bad view and replace with error message view
|
||||||
|
*/
|
||||||
|
private void handleViewError() {
|
||||||
|
removeViewInLayout(mView);
|
||||||
|
View child = getErrorView();
|
||||||
|
prepareView(child);
|
||||||
|
addViewInLayout(child, 0, child.getLayoutParams());
|
||||||
|
measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
|
||||||
|
MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
|
||||||
|
child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight,
|
||||||
|
child.getMeasuredHeight() + mPaddingTop + mPaddingBottom);
|
||||||
|
mView = child;
|
||||||
|
mViewMode = VIEW_MODE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide guidance about the size of this widget to the AppWidgetManager. The widths and
|
* Provide guidance about the size of this widget to the AppWidgetManager. The widths and
|
||||||
* heights should correspond to the full area the AppWidgetHostView is given. Padding added by
|
* heights should correspond to the full area the AppWidgetHostView is given. Padding added by
|
||||||
@@ -940,4 +948,15 @@ public class AppWidgetHostView extends FrameLayout {
|
|||||||
reapplyLastRemoteViews();
|
reapplyLastRemoteViews();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dispatchDraw(@NonNull Canvas canvas) {
|
||||||
|
try {
|
||||||
|
super.dispatchDraw(canvas);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Catch draw exceptions that may be caused by RemoteViews
|
||||||
|
Log.e(TAG, "Drawing view failed: " + e);
|
||||||
|
post(this::handleViewError);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ import android.widget.ImageView;
|
|||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.logging.MetricsLogger;
|
import com.android.internal.logging.MetricsLogger;
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||||
|
import com.android.internal.statusbar.IStatusBarService;
|
||||||
import com.android.internal.util.ContrastColorUtil;
|
import com.android.internal.util.ContrastColorUtil;
|
||||||
import com.android.internal.widget.CachingIconView;
|
import com.android.internal.widget.CachingIconView;
|
||||||
import com.android.internal.widget.CallLayout;
|
import com.android.internal.widget.CallLayout;
|
||||||
@@ -1551,7 +1552,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
NotificationGutsManager gutsManager,
|
NotificationGutsManager gutsManager,
|
||||||
MetricsLogger metricsLogger,
|
MetricsLogger metricsLogger,
|
||||||
SmartReplyConstants smartReplyConstants,
|
SmartReplyConstants smartReplyConstants,
|
||||||
SmartReplyController smartReplyController) {
|
SmartReplyController smartReplyController,
|
||||||
|
IStatusBarService statusBarService) {
|
||||||
mEntry = entry;
|
mEntry = entry;
|
||||||
mAppName = appName;
|
mAppName = appName;
|
||||||
if (mMenuRow == null) {
|
if (mMenuRow == null) {
|
||||||
@@ -1580,7 +1582,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
mPeopleNotificationIdentifier,
|
mPeopleNotificationIdentifier,
|
||||||
rivSubcomponentFactory,
|
rivSubcomponentFactory,
|
||||||
smartReplyConstants,
|
smartReplyConstants,
|
||||||
smartReplyController);
|
smartReplyController,
|
||||||
|
statusBarService);
|
||||||
}
|
}
|
||||||
mOnUserInteractionCallback = onUserInteractionCallback;
|
mOnUserInteractionCallback = onUserInteractionCallback;
|
||||||
mBubblesManagerOptional = bubblesManagerOptional;
|
mBubblesManagerOptional = bubblesManagerOptional;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.internal.logging.MetricsLogger;
|
import com.android.internal.logging.MetricsLogger;
|
||||||
|
import com.android.internal.statusbar.IStatusBarService;
|
||||||
import com.android.systemui.classifier.FalsingCollector;
|
import com.android.systemui.classifier.FalsingCollector;
|
||||||
import com.android.systemui.flags.FeatureFlags;
|
import com.android.systemui.flags.FeatureFlags;
|
||||||
import com.android.systemui.flags.Flags;
|
import com.android.systemui.flags.Flags;
|
||||||
@@ -100,8 +101,8 @@ public class ExpandableNotificationRowController implements NotifViewController
|
|||||||
private final Optional<BubblesManager> mBubblesManagerOptional;
|
private final Optional<BubblesManager> mBubblesManagerOptional;
|
||||||
private final SmartReplyConstants mSmartReplyConstants;
|
private final SmartReplyConstants mSmartReplyConstants;
|
||||||
private final SmartReplyController mSmartReplyController;
|
private final SmartReplyController mSmartReplyController;
|
||||||
|
|
||||||
private final ExpandableNotificationRowDragController mDragController;
|
private final ExpandableNotificationRowDragController mDragController;
|
||||||
|
private final IStatusBarService mStatusBarService;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ExpandableNotificationRowController(
|
public ExpandableNotificationRowController(
|
||||||
@@ -133,7 +134,8 @@ public class ExpandableNotificationRowController implements NotifViewController
|
|||||||
FeatureFlags featureFlags,
|
FeatureFlags featureFlags,
|
||||||
PeopleNotificationIdentifier peopleNotificationIdentifier,
|
PeopleNotificationIdentifier peopleNotificationIdentifier,
|
||||||
Optional<BubblesManager> bubblesManagerOptional,
|
Optional<BubblesManager> bubblesManagerOptional,
|
||||||
ExpandableNotificationRowDragController dragController) {
|
ExpandableNotificationRowDragController dragController,
|
||||||
|
IStatusBarService statusBarService) {
|
||||||
mView = view;
|
mView = view;
|
||||||
mListContainer = listContainer;
|
mListContainer = listContainer;
|
||||||
mRemoteInputViewSubcomponentFactory = rivSubcomponentFactory;
|
mRemoteInputViewSubcomponentFactory = rivSubcomponentFactory;
|
||||||
@@ -164,6 +166,7 @@ public class ExpandableNotificationRowController implements NotifViewController
|
|||||||
mMetricsLogger = metricsLogger;
|
mMetricsLogger = metricsLogger;
|
||||||
mSmartReplyConstants = smartReplyConstants;
|
mSmartReplyConstants = smartReplyConstants;
|
||||||
mSmartReplyController = smartReplyController;
|
mSmartReplyController = smartReplyController;
|
||||||
|
mStatusBarService = statusBarService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -194,7 +197,8 @@ public class ExpandableNotificationRowController implements NotifViewController
|
|||||||
mNotificationGutsManager,
|
mNotificationGutsManager,
|
||||||
mMetricsLogger,
|
mMetricsLogger,
|
||||||
mSmartReplyConstants,
|
mSmartReplyConstants,
|
||||||
mSmartReplyController
|
mSmartReplyController,
|
||||||
|
mStatusBarService
|
||||||
);
|
);
|
||||||
mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
|
mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
|
||||||
if (mAllowLongPress) {
|
if (mAllowLongPress) {
|
||||||
|
|||||||
@@ -21,10 +21,13 @@ import android.annotation.Nullable;
|
|||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.RemoteException;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
import android.service.notification.StatusBarNotification;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.IndentingPrintWriter;
|
import android.util.IndentingPrintWriter;
|
||||||
@@ -39,6 +42,7 @@ import android.widget.ImageView;
|
|||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.internal.statusbar.IStatusBarService;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
|
||||||
import com.android.systemui.statusbar.RemoteInputController;
|
import com.android.systemui.statusbar.RemoteInputController;
|
||||||
@@ -129,6 +133,7 @@ public class NotificationContentView extends FrameLayout implements Notification
|
|||||||
private Runnable mExpandedVisibleListener;
|
private Runnable mExpandedVisibleListener;
|
||||||
private PeopleNotificationIdentifier mPeopleIdentifier;
|
private PeopleNotificationIdentifier mPeopleIdentifier;
|
||||||
private RemoteInputViewSubcomponent.Factory mRemoteInputSubcomponentFactory;
|
private RemoteInputViewSubcomponent.Factory mRemoteInputSubcomponentFactory;
|
||||||
|
private IStatusBarService mStatusBarService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of listeners for when content views become inactive (i.e. not the showing view).
|
* List of listeners for when content views become inactive (i.e. not the showing view).
|
||||||
@@ -194,11 +199,13 @@ public class NotificationContentView extends FrameLayout implements Notification
|
|||||||
PeopleNotificationIdentifier peopleNotificationIdentifier,
|
PeopleNotificationIdentifier peopleNotificationIdentifier,
|
||||||
RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
|
RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
|
||||||
SmartReplyConstants smartReplyConstants,
|
SmartReplyConstants smartReplyConstants,
|
||||||
SmartReplyController smartReplyController) {
|
SmartReplyController smartReplyController,
|
||||||
|
IStatusBarService statusBarService) {
|
||||||
mPeopleIdentifier = peopleNotificationIdentifier;
|
mPeopleIdentifier = peopleNotificationIdentifier;
|
||||||
mRemoteInputSubcomponentFactory = rivSubcomponentFactory;
|
mRemoteInputSubcomponentFactory = rivSubcomponentFactory;
|
||||||
mSmartReplyConstants = smartReplyConstants;
|
mSmartReplyConstants = smartReplyConstants;
|
||||||
mSmartReplyController = smartReplyController;
|
mSmartReplyController = smartReplyController;
|
||||||
|
mStatusBarService = statusBarService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reinflate() {
|
public void reinflate() {
|
||||||
@@ -2090,4 +2097,49 @@ public class NotificationContentView extends FrameLayout implements Notification
|
|||||||
@Nullable RemoteInputView mView;
|
@Nullable RemoteInputView mView;
|
||||||
@Nullable RemoteInputViewController mController;
|
@Nullable RemoteInputViewController mController;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
protected void setContractedWrapper(NotificationViewWrapper contractedWrapper) {
|
||||||
|
mContractedWrapper = contractedWrapper;
|
||||||
|
}
|
||||||
|
@VisibleForTesting
|
||||||
|
protected void setExpandedWrapper(NotificationViewWrapper expandedWrapper) {
|
||||||
|
mExpandedWrapper = expandedWrapper;
|
||||||
|
}
|
||||||
|
@VisibleForTesting
|
||||||
|
protected void setHeadsUpWrapper(NotificationViewWrapper headsUpWrapper) {
|
||||||
|
mHeadsUpWrapper = headsUpWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dispatchDraw(Canvas canvas) {
|
||||||
|
try {
|
||||||
|
super.dispatchDraw(canvas);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Catch draw exceptions that may be caused by RemoteViews
|
||||||
|
Log.e(TAG, "Drawing view failed: " + e);
|
||||||
|
cancelNotification(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelNotification(Exception exception) {
|
||||||
|
try {
|
||||||
|
setVisibility(GONE);
|
||||||
|
final StatusBarNotification sbn = mNotificationEntry.getSbn();
|
||||||
|
if (mStatusBarService != null) {
|
||||||
|
// report notification inflation errors back up
|
||||||
|
// to notification delegates
|
||||||
|
mStatusBarService.onNotificationError(
|
||||||
|
sbn.getPackageName(),
|
||||||
|
sbn.getTag(),
|
||||||
|
sbn.getId(),
|
||||||
|
sbn.getUid(),
|
||||||
|
sbn.getInitialPid(),
|
||||||
|
exception.getMessage(),
|
||||||
|
sbn.getUser().getIdentifier());
|
||||||
|
}
|
||||||
|
} catch (RemoteException ex) {
|
||||||
|
Log.e(TAG, "cancelNotification failed: " + ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -281,7 +281,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
|
|||||||
mock(FeatureFlags.class),
|
mock(FeatureFlags.class),
|
||||||
mPeopleNotificationIdentifier,
|
mPeopleNotificationIdentifier,
|
||||||
Optional.of(mock(BubblesManager.class)),
|
Optional.of(mock(BubblesManager.class)),
|
||||||
mock(ExpandableNotificationRowDragController.class)));
|
mock(ExpandableNotificationRowDragController.class),
|
||||||
|
mock(IStatusBarService.class)));
|
||||||
|
|
||||||
when(mNotificationRowComponentBuilder.activatableNotificationView(any()))
|
when(mNotificationRowComponentBuilder.activatableNotificationView(any()))
|
||||||
.thenReturn(mNotificationRowComponentBuilder);
|
.thenReturn(mNotificationRowComponentBuilder);
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import android.view.LayoutInflater;
|
|||||||
import android.widget.RemoteViews;
|
import android.widget.RemoteViews;
|
||||||
|
|
||||||
import com.android.internal.logging.MetricsLogger;
|
import com.android.internal.logging.MetricsLogger;
|
||||||
|
import com.android.internal.statusbar.IStatusBarService;
|
||||||
import com.android.systemui.TestableDependency;
|
import com.android.systemui.TestableDependency;
|
||||||
import com.android.systemui.classifier.FalsingCollectorFake;
|
import com.android.systemui.classifier.FalsingCollectorFake;
|
||||||
import com.android.systemui.classifier.FalsingManagerFake;
|
import com.android.systemui.classifier.FalsingManagerFake;
|
||||||
@@ -523,7 +524,8 @@ public class NotificationTestHelper {
|
|||||||
mock(NotificationGutsManager.class),
|
mock(NotificationGutsManager.class),
|
||||||
mock(MetricsLogger.class),
|
mock(MetricsLogger.class),
|
||||||
mock(SmartReplyConstants.class),
|
mock(SmartReplyConstants.class),
|
||||||
mock(SmartReplyController.class));
|
mock(SmartReplyController.class),
|
||||||
|
mock(IStatusBarService.class));
|
||||||
|
|
||||||
row.setAboveShelfChangedListener(aboveShelf -> { });
|
row.setAboveShelfChangedListener(aboveShelf -> { });
|
||||||
mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags);
|
mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags);
|
||||||
|
|||||||
Reference in New Issue
Block a user