Loading core/java/android/app/Notification.java +15 −1 Original line number Diff line number Diff line Loading @@ -559,6 +559,11 @@ public class Notification implements Parcelable @SystemApi public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400; /** * @hide */ public static final int FLAG_CAN_COLORIZE = 0x00000800; public int flags; /** @hide */ Loading Loading @@ -5150,7 +5155,16 @@ public class Notification implements Parcelable if (isColorizedMedia()) { return true; } return extras.getBoolean(EXTRA_COLORIZED) && isForegroundService(); return extras.getBoolean(EXTRA_COLORIZED) && (hasColorizedPermission() || isForegroundService()); } /** * Returns whether an app can colorize due to the android.permission.USE_COLORIZED_NOTIFICATIONS * permission. The permission is checked when a notification is enqueued. */ private boolean hasColorizedPermission() { return (flags & Notification.FLAG_CAN_COLORIZE) != 0; } /** Loading core/res/AndroidManifest.xml +5 −0 Original line number Diff line number Diff line Loading @@ -3193,6 +3193,11 @@ <permission android:name="android.permission.MANAGE_NOTIFICATIONS" android:protectionLevel="signature" /> <!-- Allows notifications to be colorized <p>Not for use by third-party applications. @hide --> <permission android:name="android.permission.USE_COLORIZED_NOTIFICATIONS" android:protectionLevel="signature|setup" /> <!-- Allows access to keyguard secure storage. Only allowed for system processes. @hide --> <permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" Loading core/tests/coretests/src/android/app/NotificationTest.java +44 −3 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.app; import static com.android.internal.util.NotificationColorUtil.satisfiesTextContrast; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.content.Context; Loading @@ -26,8 +27,6 @@ import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import com.android.internal.util.NotificationColorUtil; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; Loading @@ -43,10 +42,52 @@ public class NotificationTest { mContext = InstrumentationRegistry.getContext(); } @Test public void testColorizedByPermission() { Notification n = new Notification.Builder(mContext, "test") .setFlag(Notification.FLAG_CAN_COLORIZE, true) .setColorized(true) .build(); assertTrue(n.isColorized()); n = new Notification.Builder(mContext, "test") .setFlag(Notification.FLAG_CAN_COLORIZE, true) .build(); assertFalse(n.isColorized()); n = new Notification.Builder(mContext, "test") .setFlag(Notification.FLAG_CAN_COLORIZE, false) .setColorized(true) .build(); assertFalse(n.isColorized()); } @Test public void testColorizedByForeground() { Notification n = new Notification.Builder(mContext, "test") .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) .setColorized(true) .build(); assertTrue(n.isColorized()); n = new Notification.Builder(mContext, "test") .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) .build(); assertFalse(n.isColorized()); n = new Notification.Builder(mContext, "test") .setFlag(Notification.FLAG_FOREGROUND_SERVICE, false) .setColorized(true) .build(); assertFalse(n.isColorized()); } @Test public void testColorSatisfiedWhenBgDarkTextDarker() { Notification.Builder builder = getMediaNotification(); builder.build(); Notification n = builder.build(); assertTrue(n.isColorized()); // An initial guess where the foreground color is actually darker than an already dark bg int backgroundColor = 0xff585868; Loading services/core/java/com/android/server/notification/NotificationManagerService.java +10 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.content.pm.PackageManager.FEATURE_LEANBACK; import static android.content.pm.PackageManager.FEATURE_TELEVISION; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.service.notification.NotificationListenerService .NOTIFICATION_CHANNEL_OR_GROUP_ADDED; import static android.service.notification.NotificationListenerService Loading Loading @@ -3174,6 +3175,15 @@ public class NotificationManagerService extends SystemService { pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId); Notification.addFieldsFromContext(ai, notification); int canColorize = mPackageManagerClient.checkPermission( android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg); if (canColorize == PERMISSION_GRANTED) { notification.flags |= Notification.FLAG_CAN_COLORIZE; } else { notification.flags &= ~Notification.FLAG_CAN_COLORIZE; } } catch (NameNotFoundException e) { Slog.e(TAG, "Cannot create a context for sending app", e); return; Loading services/core/java/com/android/server/notification/NotificationRecord.java +4 −2 Original line number Diff line number Diff line Loading @@ -386,6 +386,7 @@ public final class NotificationRecord { prefix = prefix + " "; pw.println(prefix + "uid=" + sbn.getUid() + " userId=" + sbn.getUserId()); pw.println(prefix + "icon=" + iconStr); pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags)); pw.println(prefix + "pri=" + notification.priority); pw.println(prefix + "key=" + sbn.getKey()); pw.println(prefix + "seen=" + mIsSeen); Loading Loading @@ -495,6 +496,7 @@ public final class NotificationRecord { pw.println(prefix + "mAttributes= " + mAttributes); pw.println(prefix + "mLight= " + mLight); pw.println(prefix + "mShowBadge=" + mShowBadge); pw.println(prefix + "mColorized=" + notification.isColorized()); pw.println(prefix + "effectiveNotificationChannel=" + getChannel()); if (getPeopleOverride() != null) { pw.println(prefix + "overridePeople= " + TextUtils.join(",", getPeopleOverride())); Loading Loading @@ -530,10 +532,10 @@ public final class NotificationRecord { public final String toString() { return String.format( "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s" + " channel=%s: %s)", ": %s)", System.identityHashCode(this), this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(), this.sbn.getTag(), this.mImportance, this.sbn.getKey(), this.getChannel().getId(), this.sbn.getTag(), this.mImportance, this.sbn.getKey(), this.sbn.getNotification()); } Loading Loading
core/java/android/app/Notification.java +15 −1 Original line number Diff line number Diff line Loading @@ -559,6 +559,11 @@ public class Notification implements Parcelable @SystemApi public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400; /** * @hide */ public static final int FLAG_CAN_COLORIZE = 0x00000800; public int flags; /** @hide */ Loading Loading @@ -5150,7 +5155,16 @@ public class Notification implements Parcelable if (isColorizedMedia()) { return true; } return extras.getBoolean(EXTRA_COLORIZED) && isForegroundService(); return extras.getBoolean(EXTRA_COLORIZED) && (hasColorizedPermission() || isForegroundService()); } /** * Returns whether an app can colorize due to the android.permission.USE_COLORIZED_NOTIFICATIONS * permission. The permission is checked when a notification is enqueued. */ private boolean hasColorizedPermission() { return (flags & Notification.FLAG_CAN_COLORIZE) != 0; } /** Loading
core/res/AndroidManifest.xml +5 −0 Original line number Diff line number Diff line Loading @@ -3193,6 +3193,11 @@ <permission android:name="android.permission.MANAGE_NOTIFICATIONS" android:protectionLevel="signature" /> <!-- Allows notifications to be colorized <p>Not for use by third-party applications. @hide --> <permission android:name="android.permission.USE_COLORIZED_NOTIFICATIONS" android:protectionLevel="signature|setup" /> <!-- Allows access to keyguard secure storage. Only allowed for system processes. @hide --> <permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" Loading
core/tests/coretests/src/android/app/NotificationTest.java +44 −3 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.app; import static com.android.internal.util.NotificationColorUtil.satisfiesTextContrast; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.content.Context; Loading @@ -26,8 +27,6 @@ import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import com.android.internal.util.NotificationColorUtil; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; Loading @@ -43,10 +42,52 @@ public class NotificationTest { mContext = InstrumentationRegistry.getContext(); } @Test public void testColorizedByPermission() { Notification n = new Notification.Builder(mContext, "test") .setFlag(Notification.FLAG_CAN_COLORIZE, true) .setColorized(true) .build(); assertTrue(n.isColorized()); n = new Notification.Builder(mContext, "test") .setFlag(Notification.FLAG_CAN_COLORIZE, true) .build(); assertFalse(n.isColorized()); n = new Notification.Builder(mContext, "test") .setFlag(Notification.FLAG_CAN_COLORIZE, false) .setColorized(true) .build(); assertFalse(n.isColorized()); } @Test public void testColorizedByForeground() { Notification n = new Notification.Builder(mContext, "test") .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) .setColorized(true) .build(); assertTrue(n.isColorized()); n = new Notification.Builder(mContext, "test") .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) .build(); assertFalse(n.isColorized()); n = new Notification.Builder(mContext, "test") .setFlag(Notification.FLAG_FOREGROUND_SERVICE, false) .setColorized(true) .build(); assertFalse(n.isColorized()); } @Test public void testColorSatisfiedWhenBgDarkTextDarker() { Notification.Builder builder = getMediaNotification(); builder.build(); Notification n = builder.build(); assertTrue(n.isColorized()); // An initial guess where the foreground color is actually darker than an already dark bg int backgroundColor = 0xff585868; Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +10 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.content.pm.PackageManager.FEATURE_LEANBACK; import static android.content.pm.PackageManager.FEATURE_TELEVISION; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.service.notification.NotificationListenerService .NOTIFICATION_CHANNEL_OR_GROUP_ADDED; import static android.service.notification.NotificationListenerService Loading Loading @@ -3174,6 +3175,15 @@ public class NotificationManagerService extends SystemService { pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId); Notification.addFieldsFromContext(ai, notification); int canColorize = mPackageManagerClient.checkPermission( android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg); if (canColorize == PERMISSION_GRANTED) { notification.flags |= Notification.FLAG_CAN_COLORIZE; } else { notification.flags &= ~Notification.FLAG_CAN_COLORIZE; } } catch (NameNotFoundException e) { Slog.e(TAG, "Cannot create a context for sending app", e); return; Loading
services/core/java/com/android/server/notification/NotificationRecord.java +4 −2 Original line number Diff line number Diff line Loading @@ -386,6 +386,7 @@ public final class NotificationRecord { prefix = prefix + " "; pw.println(prefix + "uid=" + sbn.getUid() + " userId=" + sbn.getUserId()); pw.println(prefix + "icon=" + iconStr); pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags)); pw.println(prefix + "pri=" + notification.priority); pw.println(prefix + "key=" + sbn.getKey()); pw.println(prefix + "seen=" + mIsSeen); Loading Loading @@ -495,6 +496,7 @@ public final class NotificationRecord { pw.println(prefix + "mAttributes= " + mAttributes); pw.println(prefix + "mLight= " + mLight); pw.println(prefix + "mShowBadge=" + mShowBadge); pw.println(prefix + "mColorized=" + notification.isColorized()); pw.println(prefix + "effectiveNotificationChannel=" + getChannel()); if (getPeopleOverride() != null) { pw.println(prefix + "overridePeople= " + TextUtils.join(",", getPeopleOverride())); Loading Loading @@ -530,10 +532,10 @@ public final class NotificationRecord { public final String toString() { return String.format( "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s" + " channel=%s: %s)", ": %s)", System.identityHashCode(this), this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(), this.sbn.getTag(), this.mImportance, this.sbn.getKey(), this.getChannel().getId(), this.sbn.getTag(), this.mImportance, this.sbn.getKey(), this.sbn.getNotification()); } Loading