summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_row.xml7
-rw-r--r--packages/SystemUI/res/values/dimens.xml9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java72
-rw-r--r--packages/SystemUI/tests/res/layout/invalid_notification_height.xml18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java34
5 files changed, 138 insertions, 2 deletions
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 2c08f5db0323..356b36fdbcd6 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -39,8 +39,11 @@
<com.android.systemui.statusbar.notification.row.NotificationContentView
android:id="@+id/expanded"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_content_min_height"
+ android:gravity="center_vertical"
+ />
<com.android.systemui.statusbar.notification.row.NotificationContentView
android:id="@+id/expandedPublic"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d492e53c5390..939171479d32 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -184,6 +184,15 @@
<!-- Height of a small notification in the status bar-->
<dimen name="notification_min_height">@*android:dimen/notification_min_height</dimen>
+ <!-- Minimum allowed height of notifications -->
+ <dimen name="notification_validation_minimum_allowed_height">10dp</dimen>
+
+ <!-- Minimum height for displaying notification content. -->
+ <dimen name="notification_content_min_height">48dp</dimen>
+
+ <!-- Reference width used when validating notification layouts -->
+ <dimen name="notification_validation_reference_width">320dp</dimen>
+
<!-- Increased height of a small notification in the status bar -->
<dimen name="notification_min_height_increased">146dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index c534860d12c6..39e4000c5d05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -28,8 +28,11 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.CancellationSignal;
+import android.os.Trace;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.util.Log;
@@ -38,6 +41,7 @@ import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ImageMessageConsumer;
+import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
@@ -468,6 +472,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
result.packageContext,
parentLayout,
remoteViewClickHandler);
+ validateView(v, entry, row.getResources());
v.setIsRootNamespace(true);
applyCallback.setResultView(v);
} else {
@@ -475,6 +480,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
result.packageContext,
existingView,
remoteViewClickHandler);
+ validateView(existingView, entry, row.getResources());
existingWrapper.onReinflated();
}
} catch (Exception e) {
@@ -496,6 +502,13 @@ public class NotificationContentInflater implements NotificationRowContentBinder
@Override
public void onViewApplied(View v) {
+ String invalidReason = isValidView(v, entry, row.getResources());
+ if (invalidReason != null) {
+ handleInflationError(runningInflations, new InflationException(invalidReason),
+ row.getEntry(), callback);
+ runningInflations.remove(inflationId);
+ return;
+ }
if (isNewView) {
v.setIsRootNamespace(true);
applyCallback.setResultView(v);
@@ -553,6 +566,65 @@ public class NotificationContentInflater implements NotificationRowContentBinder
runningInflations.put(inflationId, cancellationSignal);
}
+ /**
+ * Checks if the given View is a valid notification View.
+ *
+ * @return null == valid, non-null == invalid, String represents reason for rejection.
+ */
+ @VisibleForTesting
+ @Nullable
+ static String isValidView(View view,
+ NotificationEntry entry,
+ Resources resources) {
+ if (!satisfiesMinHeightRequirement(view, entry, resources)) {
+ return "inflated notification does not meet minimum height requirement";
+ }
+ return null;
+ }
+
+ private static boolean satisfiesMinHeightRequirement(View view,
+ NotificationEntry entry,
+ Resources resources) {
+ if (!requiresHeightCheck(entry)) {
+ return true;
+ }
+ Trace.beginSection("NotificationContentInflater#satisfiesMinHeightRequirement");
+ int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+ int referenceWidth = resources.getDimensionPixelSize(
+ R.dimen.notification_validation_reference_width);
+ int widthSpec = View.MeasureSpec.makeMeasureSpec(referenceWidth, View.MeasureSpec.EXACTLY);
+ view.measure(widthSpec, heightSpec);
+ int minHeight = resources.getDimensionPixelSize(
+ R.dimen.notification_validation_minimum_allowed_height);
+ boolean result = view.getMeasuredHeight() >= minHeight;
+ Trace.endSection();
+ return result;
+ }
+
+ private static boolean requiresHeightCheck(NotificationEntry entry) {
+ // Undecorated custom views are disallowed from S onwards
+ if (entry.targetSdk >= Build.VERSION_CODES.S) {
+ return false;
+ }
+ // No need to check if the app isn't using any custom views
+ Notification notification = entry.getSbn().getNotification();
+ if (notification.contentView == null
+ && notification.bigContentView == null
+ && notification.headsUpContentView == null) {
+ return false;
+ }
+ return true;
+ }
+
+ private static void validateView(View view,
+ NotificationEntry entry,
+ Resources resources) throws InflationException {
+ String invalidReason = isValidView(view, entry, resources);
+ if (invalidReason != null) {
+ throw new InflationException(invalidReason);
+ }
+ }
+
private static void handleInflationError(
HashMap<Integer, CancellationSignal> runningInflations, Exception e,
NotificationEntry notification, @Nullable InflationCallback callback) {
diff --git a/packages/SystemUI/tests/res/layout/invalid_notification_height.xml b/packages/SystemUI/tests/res/layout/invalid_notification_height.xml
new file mode 100644
index 000000000000..aac43bf45676
--- /dev/null
+++ b/packages/SystemUI/tests/res/layout/invalid_notification_height.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="5dp"/> \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 5394d88ad103..3face350526a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -24,6 +24,7 @@ import static com.android.systemui.statusbar.notification.row.NotificationRowCon
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -42,6 +43,7 @@ import android.os.Looper;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RemoteViews;
@@ -332,6 +334,38 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
eq(FLAG_CONTENT_VIEW_HEADS_UP));
}
+ @Test
+ public void testNotificationViewHeightTooSmallFailsValidation() {
+ View view = mock(View.class);
+ when(view.getHeight())
+ .thenReturn((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10,
+ mContext.getResources().getDisplayMetrics()));
+ String result = NotificationContentInflater.isValidView(view, mRow.getEntry(),
+ mContext.getResources());
+ assertNotNull(result);
+ }
+
+ @Test
+ public void testNotificationViewPassesValidation() {
+ View view = mock(View.class);
+ when(view.getHeight())
+ .thenReturn((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 17,
+ mContext.getResources().getDisplayMetrics()));
+ String result = NotificationContentInflater.isValidView(view, mRow.getEntry(),
+ mContext.getResources());
+ assertNull(result);
+ }
+
+ @Test
+ public void testInvalidNotificationDoesNotInvokeCallback() throws Exception {
+ mRow.getPrivateLayout().removeAllViews();
+ mRow.getEntry().getSbn().getNotification().contentView =
+ new RemoteViews(mContext.getPackageName(), R.layout.invalid_notification_height);
+ inflateAndWait(true, mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
+ assertEquals(0, mRow.getPrivateLayout().getChildCount());
+ verify(mRow, times(0)).onNotificationUpdated();
+ }
+
private static void inflateAndWait(NotificationContentInflater inflater,
@InflationFlag int contentToInflate,
ExpandableNotificationRow row)