Loading core/java/android/app/NotificationChannel.java +81 −3 Original line number Diff line number Diff line Loading @@ -15,8 +15,11 @@ */ package android.app; import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.NotificationManager.Importance; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.media.AudioAttributes; import android.net.Uri; Loading @@ -27,6 +30,8 @@ import android.service.notification.NotificationListenerService; import android.text.TextUtils; import android.util.proto.ProtoOutputStream; import com.android.internal.util.Preconditions; import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; Loading Loading @@ -566,17 +571,38 @@ public final class NotificationChannel implements Parcelable { return mBlockableSystem; } /** * @hide */ public void populateFromXmlForRestore(XmlPullParser parser, Context context) { populateFromXml(parser, true, context); } /** * @hide */ @SystemApi public void populateFromXml(XmlPullParser parser) { populateFromXml(parser, false, null); } /** * If {@param forRestore} is true, {@param Context} MUST be non-null. */ private void populateFromXml(XmlPullParser parser, boolean forRestore, @Nullable Context context) { Preconditions.checkArgument(!forRestore || context != null, "forRestore is true but got null context"); // Name, id, and importance are set in the constructor. setDescription(parser.getAttributeValue(null, ATT_DESC)); setBypassDnd(Notification.PRIORITY_DEFAULT != safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT)); setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY)); setSound(safeUri(parser, ATT_SOUND), safeAudioAttributes(parser)); Uri sound = safeUri(parser, ATT_SOUND); setSound(forRestore ? restoreSoundUri(context, sound) : sound, safeAudioAttributes(parser)); enableLights(safeBool(parser, ATT_LIGHTS, false)); setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR)); setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null)); Loading @@ -588,11 +614,62 @@ public final class NotificationChannel implements Parcelable { setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false)); } @Nullable private Uri restoreSoundUri(Context context, @Nullable Uri uri) { if (uri == null) { return null; } ContentResolver contentResolver = context.getContentResolver(); // There are backups out there with uncanonical uris (because we fixed this after // shipping). If uncanonical uris are given to MediaProvider.uncanonicalize it won't // verify the uri against device storage and we'll possibly end up with a broken uri. // We then canonicalize the uri to uncanonicalize it back, which means we properly check // the uri and in the case of not having the resource we end up with the default - better // than broken. As a side effect we'll canonicalize already canonicalized uris, this is fine // according to the docs because canonicalize method has to handle canonical uris as well. Uri canonicalizedUri = contentResolver.canonicalize(uri); if (canonicalizedUri == null) { // We got a null because the uri in the backup does not exist here, so we return default return Settings.System.DEFAULT_NOTIFICATION_URI; } return contentResolver.uncanonicalize(canonicalizedUri); } /** * @hide */ @SystemApi public void writeXml(XmlSerializer out) throws IOException { writeXml(out, false, null); } /** * @hide */ public void writeXmlForBackup(XmlSerializer out, Context context) throws IOException { writeXml(out, true, context); } private Uri getSoundForBackup(Context context) { Uri sound = getSound(); if (sound == null) { return null; } Uri canonicalSound = context.getContentResolver().canonicalize(sound); if (canonicalSound == null) { // The content provider does not support canonical uris so we backup the default return Settings.System.DEFAULT_NOTIFICATION_URI; } return canonicalSound; } /** * If {@param forBackup} is true, {@param Context} MUST be non-null. */ private void writeXml(XmlSerializer out, boolean forBackup, @Nullable Context context) throws IOException { Preconditions.checkArgument(!forBackup || context != null, "forBackup is true but got null context"); out.startTag(null, TAG_CHANNEL); out.attribute(null, ATT_ID, getId()); if (getName() != null) { Loading @@ -613,8 +690,9 @@ public final class NotificationChannel implements Parcelable { out.attribute(null, ATT_VISIBILITY, Integer.toString(getLockscreenVisibility())); } if (getSound() != null) { out.attribute(null, ATT_SOUND, getSound().toString()); Uri sound = forBackup ? getSoundForBackup(context) : getSound(); if (sound != null) { out.attribute(null, ATT_SOUND, sound.toString()); } if (getAudioAttributes() != null) { out.attribute(null, ATT_USAGE, Integer.toString(getAudioAttributes().getUsage())); Loading services/core/java/com/android/server/notification/RankingHelper.java +10 −2 Original line number Diff line number Diff line Loading @@ -231,7 +231,11 @@ public class RankingHelper implements RankingConfig { if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) { NotificationChannel channel = new NotificationChannel(id, channelName, channelImportance); if (forRestore) { channel.populateFromXmlForRestore(parser, mContext); } else { channel.populateFromXml(parser); } r.channels.put(id, channel); } } Loading Loading @@ -394,7 +398,11 @@ public class RankingHelper implements RankingConfig { } for (NotificationChannel channel : r.channels.values()) { if (!forBackup || (forBackup && !channel.isDeleted())) { if (forBackup) { if (!channel.isDeleted()) { channel.writeXmlForBackup(out, mContext); } } else { channel.writeXml(out); } } Loading services/tests/notification/src/com/android/server/notification/RankingHelperTest.java +149 −20 Original line number Diff line number Diff line Loading @@ -25,25 +25,13 @@ import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.fail; import org.json.JSONArray; import org.json.JSONObject; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import com.android.internal.util.FastXmlSerializer; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; import android.app.Notification; import android.app.NotificationChannelGroup; import android.content.Context; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.content.ContentProvider; import android.content.Context; import android.content.IContentProvider; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Resources; Loading @@ -52,14 +40,28 @@ import android.media.AudioAttributes; import android.net.Uri; import android.os.Build; import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.Secure; import android.service.notification.StatusBarNotification; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import android.testing.TestableContentResolver; import android.util.ArrayMap; import android.util.Xml; import com.android.internal.util.FastXmlSerializer; import org.json.JSONArray; import org.json.JSONObject; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; Loading @@ -76,6 +78,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; Loading @@ -95,10 +98,17 @@ public class RankingHelperTest extends NotificationTestCase { private static final int UID2 = 1111; private static final UserHandle USER2 = UserHandle.of(10); private static final String TEST_CHANNEL_ID = "test_channel_id"; private static final String TEST_AUTHORITY = "test"; private static final Uri SOUND_URI = Uri.parse("content://" + TEST_AUTHORITY + "/internal/audio/media/10"); private static final Uri CANONICAL_SOUND_URI = Uri.parse("content://" + TEST_AUTHORITY + "/internal/audio/media/10?title=Test&canonical=1"); @Mock NotificationUsageStats mUsageStats; @Mock RankingHandler mHandler; @Mock PackageManager mPm; @Mock IContentProvider mTestIContentProvider; @Mock Context mContext; private Notification mNotiGroupGSortA; Loading Loading @@ -134,9 +144,22 @@ public class RankingHelperTest extends NotificationTestCase { when(mContext.getPackageManager()).thenReturn(mPm); when(mContext.getApplicationInfo()).thenReturn(legacy); // most tests assume badging is enabled Secure.putIntForUser(getContext().getContentResolver(), TestableContentResolver contentResolver = getContext().getContentResolver(); contentResolver.setFallbackToExisting(false); Secure.putIntForUser(contentResolver, Secure.NOTIFICATION_BADGING, 1, UserHandle.getUserId(UID)); ContentProvider testContentProvider = mock(ContentProvider.class); when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider); contentResolver.addProvider(TEST_AUTHORITY, testContentProvider); when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))) .thenReturn(CANONICAL_SOUND_URI); when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI))) .thenReturn(CANONICAL_SOUND_URI); when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI))) .thenReturn(SOUND_URI); mHelper = new RankingHelper(getContext(), mPm, mHandler, mUsageStats, new String[] {ImportanceExtractor.class.getName()}); Loading Loading @@ -214,9 +237,12 @@ public class RankingHelperTest extends NotificationTestCase { } private void loadStreamXml(ByteArrayOutputStream stream, boolean forRestore) throws Exception { loadByteArrayXml(stream.toByteArray(), forRestore); } private void loadByteArrayXml(byte[] byteArray, boolean forRestore) throws Exception { XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(stream.toByteArray())), null); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null); parser.nextTag(); mHelper.readXml(parser, forRestore); } Loading Loading @@ -377,7 +403,7 @@ public class RankingHelperTest extends NotificationTestCase { NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW); channel2.setDescription("descriptions for all"); channel2.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes); channel2.setSound(SOUND_URI, mAudioAttributes); channel2.enableLights(true); channel2.setBypassDnd(true); channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET); Loading Loading @@ -438,6 +464,109 @@ public class RankingHelperTest extends NotificationTestCase { assertTrue(foundChannel2Group); } @Test public void testBackupXml_backupCanonicalizedSoundUri() throws Exception { NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(SOUND_URI, mAudioAttributes); mHelper.createNotificationChannel(PKG, UID, channel, true); ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId()); // Testing that in restore we are given the canonical version loadStreamXml(baos, true); verify(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI)); } @Test public void testRestoreXml_withExistentCanonicalizedSoundUri() throws Exception { Uri localUri = Uri.parse("content://" + TEST_AUTHORITY + "/local/url"); Uri canonicalBasedOnLocal = localUri.buildUpon() .appendQueryParameter("title", "Test") .appendQueryParameter("canonical", "1") .build(); when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI))) .thenReturn(canonicalBasedOnLocal); when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI))) .thenReturn(localUri); when(mTestIContentProvider.uncanonicalize(any(), eq(canonicalBasedOnLocal))) .thenReturn(localUri); NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(SOUND_URI, mAudioAttributes); mHelper.createNotificationChannel(PKG, UID, channel, true); ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId()); loadStreamXml(baos, true); NotificationChannel actualChannel = mHelper.getNotificationChannel( PKG, UID, channel.getId(), false); assertEquals(localUri, actualChannel.getSound()); } @Test public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception { Thread.sleep(3000); when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI))) .thenReturn(null); when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI))) .thenReturn(null); NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(SOUND_URI, mAudioAttributes); mHelper.createNotificationChannel(PKG, UID, channel, true); ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId()); loadStreamXml(baos, true); NotificationChannel actualChannel = mHelper.getNotificationChannel( PKG, UID, channel.getId(), false); assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound()); } /** * Although we don't make backups with uncanonicalized uris anymore, we used to, so we have to * handle its restore properly. */ @Test public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception { // Not a local uncanonicalized uri, simulating that it fails to exist locally when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))).thenReturn(null); String id = "id"; String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n" + "<package name=\"com.android.server.notification\" show_badge=\"true\">\n" + "<channel id=\"" + id + "\" name=\"name\" importance=\"2\" " + "sound=\"" + SOUND_URI + "\" " + "usage=\"6\" content_type=\"0\" flags=\"1\" show_badge=\"true\" />\n" + "<channel id=\"miscellaneous\" name=\"Uncategorized\" usage=\"5\" " + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" + "</package>\n" + "</ranking>\n"; loadByteArrayXml(backupWithUncanonicalizedSoundUri.getBytes(), true); NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG, UID, id, false); assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound()); } @Test public void testBackupRestoreXml_withNullSoundUri() throws Exception { NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(null, mAudioAttributes); mHelper.createNotificationChannel(PKG, UID, channel, true); ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId()); loadStreamXml(baos, true); NotificationChannel actualChannel = mHelper.getNotificationChannel( PKG, UID, channel.getId(), false); assertEquals(null, actualChannel.getSound()); } @Test public void testChannelXml_backup() throws Exception { NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye"); Loading Loading
core/java/android/app/NotificationChannel.java +81 −3 Original line number Diff line number Diff line Loading @@ -15,8 +15,11 @@ */ package android.app; import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.NotificationManager.Importance; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.media.AudioAttributes; import android.net.Uri; Loading @@ -27,6 +30,8 @@ import android.service.notification.NotificationListenerService; import android.text.TextUtils; import android.util.proto.ProtoOutputStream; import com.android.internal.util.Preconditions; import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; Loading Loading @@ -566,17 +571,38 @@ public final class NotificationChannel implements Parcelable { return mBlockableSystem; } /** * @hide */ public void populateFromXmlForRestore(XmlPullParser parser, Context context) { populateFromXml(parser, true, context); } /** * @hide */ @SystemApi public void populateFromXml(XmlPullParser parser) { populateFromXml(parser, false, null); } /** * If {@param forRestore} is true, {@param Context} MUST be non-null. */ private void populateFromXml(XmlPullParser parser, boolean forRestore, @Nullable Context context) { Preconditions.checkArgument(!forRestore || context != null, "forRestore is true but got null context"); // Name, id, and importance are set in the constructor. setDescription(parser.getAttributeValue(null, ATT_DESC)); setBypassDnd(Notification.PRIORITY_DEFAULT != safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT)); setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY)); setSound(safeUri(parser, ATT_SOUND), safeAudioAttributes(parser)); Uri sound = safeUri(parser, ATT_SOUND); setSound(forRestore ? restoreSoundUri(context, sound) : sound, safeAudioAttributes(parser)); enableLights(safeBool(parser, ATT_LIGHTS, false)); setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR)); setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null)); Loading @@ -588,11 +614,62 @@ public final class NotificationChannel implements Parcelable { setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false)); } @Nullable private Uri restoreSoundUri(Context context, @Nullable Uri uri) { if (uri == null) { return null; } ContentResolver contentResolver = context.getContentResolver(); // There are backups out there with uncanonical uris (because we fixed this after // shipping). If uncanonical uris are given to MediaProvider.uncanonicalize it won't // verify the uri against device storage and we'll possibly end up with a broken uri. // We then canonicalize the uri to uncanonicalize it back, which means we properly check // the uri and in the case of not having the resource we end up with the default - better // than broken. As a side effect we'll canonicalize already canonicalized uris, this is fine // according to the docs because canonicalize method has to handle canonical uris as well. Uri canonicalizedUri = contentResolver.canonicalize(uri); if (canonicalizedUri == null) { // We got a null because the uri in the backup does not exist here, so we return default return Settings.System.DEFAULT_NOTIFICATION_URI; } return contentResolver.uncanonicalize(canonicalizedUri); } /** * @hide */ @SystemApi public void writeXml(XmlSerializer out) throws IOException { writeXml(out, false, null); } /** * @hide */ public void writeXmlForBackup(XmlSerializer out, Context context) throws IOException { writeXml(out, true, context); } private Uri getSoundForBackup(Context context) { Uri sound = getSound(); if (sound == null) { return null; } Uri canonicalSound = context.getContentResolver().canonicalize(sound); if (canonicalSound == null) { // The content provider does not support canonical uris so we backup the default return Settings.System.DEFAULT_NOTIFICATION_URI; } return canonicalSound; } /** * If {@param forBackup} is true, {@param Context} MUST be non-null. */ private void writeXml(XmlSerializer out, boolean forBackup, @Nullable Context context) throws IOException { Preconditions.checkArgument(!forBackup || context != null, "forBackup is true but got null context"); out.startTag(null, TAG_CHANNEL); out.attribute(null, ATT_ID, getId()); if (getName() != null) { Loading @@ -613,8 +690,9 @@ public final class NotificationChannel implements Parcelable { out.attribute(null, ATT_VISIBILITY, Integer.toString(getLockscreenVisibility())); } if (getSound() != null) { out.attribute(null, ATT_SOUND, getSound().toString()); Uri sound = forBackup ? getSoundForBackup(context) : getSound(); if (sound != null) { out.attribute(null, ATT_SOUND, sound.toString()); } if (getAudioAttributes() != null) { out.attribute(null, ATT_USAGE, Integer.toString(getAudioAttributes().getUsage())); Loading
services/core/java/com/android/server/notification/RankingHelper.java +10 −2 Original line number Diff line number Diff line Loading @@ -231,7 +231,11 @@ public class RankingHelper implements RankingConfig { if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) { NotificationChannel channel = new NotificationChannel(id, channelName, channelImportance); if (forRestore) { channel.populateFromXmlForRestore(parser, mContext); } else { channel.populateFromXml(parser); } r.channels.put(id, channel); } } Loading Loading @@ -394,7 +398,11 @@ public class RankingHelper implements RankingConfig { } for (NotificationChannel channel : r.channels.values()) { if (!forBackup || (forBackup && !channel.isDeleted())) { if (forBackup) { if (!channel.isDeleted()) { channel.writeXmlForBackup(out, mContext); } } else { channel.writeXml(out); } } Loading
services/tests/notification/src/com/android/server/notification/RankingHelperTest.java +149 −20 Original line number Diff line number Diff line Loading @@ -25,25 +25,13 @@ import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.fail; import org.json.JSONArray; import org.json.JSONObject; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import com.android.internal.util.FastXmlSerializer; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; import android.app.Notification; import android.app.NotificationChannelGroup; import android.content.Context; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.content.ContentProvider; import android.content.Context; import android.content.IContentProvider; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Resources; Loading @@ -52,14 +40,28 @@ import android.media.AudioAttributes; import android.net.Uri; import android.os.Build; import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.Secure; import android.service.notification.StatusBarNotification; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import android.testing.TestableContentResolver; import android.util.ArrayMap; import android.util.Xml; import com.android.internal.util.FastXmlSerializer; import org.json.JSONArray; import org.json.JSONObject; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; Loading @@ -76,6 +78,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; Loading @@ -95,10 +98,17 @@ public class RankingHelperTest extends NotificationTestCase { private static final int UID2 = 1111; private static final UserHandle USER2 = UserHandle.of(10); private static final String TEST_CHANNEL_ID = "test_channel_id"; private static final String TEST_AUTHORITY = "test"; private static final Uri SOUND_URI = Uri.parse("content://" + TEST_AUTHORITY + "/internal/audio/media/10"); private static final Uri CANONICAL_SOUND_URI = Uri.parse("content://" + TEST_AUTHORITY + "/internal/audio/media/10?title=Test&canonical=1"); @Mock NotificationUsageStats mUsageStats; @Mock RankingHandler mHandler; @Mock PackageManager mPm; @Mock IContentProvider mTestIContentProvider; @Mock Context mContext; private Notification mNotiGroupGSortA; Loading Loading @@ -134,9 +144,22 @@ public class RankingHelperTest extends NotificationTestCase { when(mContext.getPackageManager()).thenReturn(mPm); when(mContext.getApplicationInfo()).thenReturn(legacy); // most tests assume badging is enabled Secure.putIntForUser(getContext().getContentResolver(), TestableContentResolver contentResolver = getContext().getContentResolver(); contentResolver.setFallbackToExisting(false); Secure.putIntForUser(contentResolver, Secure.NOTIFICATION_BADGING, 1, UserHandle.getUserId(UID)); ContentProvider testContentProvider = mock(ContentProvider.class); when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider); contentResolver.addProvider(TEST_AUTHORITY, testContentProvider); when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))) .thenReturn(CANONICAL_SOUND_URI); when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI))) .thenReturn(CANONICAL_SOUND_URI); when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI))) .thenReturn(SOUND_URI); mHelper = new RankingHelper(getContext(), mPm, mHandler, mUsageStats, new String[] {ImportanceExtractor.class.getName()}); Loading Loading @@ -214,9 +237,12 @@ public class RankingHelperTest extends NotificationTestCase { } private void loadStreamXml(ByteArrayOutputStream stream, boolean forRestore) throws Exception { loadByteArrayXml(stream.toByteArray(), forRestore); } private void loadByteArrayXml(byte[] byteArray, boolean forRestore) throws Exception { XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(stream.toByteArray())), null); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null); parser.nextTag(); mHelper.readXml(parser, forRestore); } Loading Loading @@ -377,7 +403,7 @@ public class RankingHelperTest extends NotificationTestCase { NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW); channel2.setDescription("descriptions for all"); channel2.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes); channel2.setSound(SOUND_URI, mAudioAttributes); channel2.enableLights(true); channel2.setBypassDnd(true); channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET); Loading Loading @@ -438,6 +464,109 @@ public class RankingHelperTest extends NotificationTestCase { assertTrue(foundChannel2Group); } @Test public void testBackupXml_backupCanonicalizedSoundUri() throws Exception { NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(SOUND_URI, mAudioAttributes); mHelper.createNotificationChannel(PKG, UID, channel, true); ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId()); // Testing that in restore we are given the canonical version loadStreamXml(baos, true); verify(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI)); } @Test public void testRestoreXml_withExistentCanonicalizedSoundUri() throws Exception { Uri localUri = Uri.parse("content://" + TEST_AUTHORITY + "/local/url"); Uri canonicalBasedOnLocal = localUri.buildUpon() .appendQueryParameter("title", "Test") .appendQueryParameter("canonical", "1") .build(); when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI))) .thenReturn(canonicalBasedOnLocal); when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI))) .thenReturn(localUri); when(mTestIContentProvider.uncanonicalize(any(), eq(canonicalBasedOnLocal))) .thenReturn(localUri); NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(SOUND_URI, mAudioAttributes); mHelper.createNotificationChannel(PKG, UID, channel, true); ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId()); loadStreamXml(baos, true); NotificationChannel actualChannel = mHelper.getNotificationChannel( PKG, UID, channel.getId(), false); assertEquals(localUri, actualChannel.getSound()); } @Test public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception { Thread.sleep(3000); when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI))) .thenReturn(null); when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI))) .thenReturn(null); NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(SOUND_URI, mAudioAttributes); mHelper.createNotificationChannel(PKG, UID, channel, true); ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId()); loadStreamXml(baos, true); NotificationChannel actualChannel = mHelper.getNotificationChannel( PKG, UID, channel.getId(), false); assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound()); } /** * Although we don't make backups with uncanonicalized uris anymore, we used to, so we have to * handle its restore properly. */ @Test public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception { // Not a local uncanonicalized uri, simulating that it fails to exist locally when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))).thenReturn(null); String id = "id"; String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n" + "<package name=\"com.android.server.notification\" show_badge=\"true\">\n" + "<channel id=\"" + id + "\" name=\"name\" importance=\"2\" " + "sound=\"" + SOUND_URI + "\" " + "usage=\"6\" content_type=\"0\" flags=\"1\" show_badge=\"true\" />\n" + "<channel id=\"miscellaneous\" name=\"Uncategorized\" usage=\"5\" " + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" + "</package>\n" + "</ranking>\n"; loadByteArrayXml(backupWithUncanonicalizedSoundUri.getBytes(), true); NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG, UID, id, false); assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound()); } @Test public void testBackupRestoreXml_withNullSoundUri() throws Exception { NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(null, mAudioAttributes); mHelper.createNotificationChannel(PKG, UID, channel, true); ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId()); loadStreamXml(baos, true); NotificationChannel actualChannel = mHelper.getNotificationChannel( PKG, UID, channel.getId(), false); assertEquals(null, actualChannel.getSound()); } @Test public void testChannelXml_backup() throws Exception { NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye"); Loading