Loading core/java/android/app/NotificationChannel.java +75 −11 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; Loading Loading @@ -246,6 +247,7 @@ public final class NotificationChannel implements Parcelable { private boolean mBypassDnd; private int mLockscreenVisibility = DEFAULT_VISIBILITY; private Uri mSound = Settings.System.DEFAULT_NOTIFICATION_URI; private boolean mSoundRestored = false; private boolean mLights; private int mLightColor = DEFAULT_LIGHT_COLOR; private long[] mVibration; Loading Loading @@ -929,8 +931,9 @@ public final class NotificationChannel implements Parcelable { /** * @hide */ public void populateFromXmlForRestore(XmlPullParser parser, Context context) { populateFromXml(XmlUtils.makeTyped(parser), true, context); public void populateFromXmlForRestore(XmlPullParser parser, boolean pkgInstalled, Context context) { populateFromXml(XmlUtils.makeTyped(parser), true, pkgInstalled, context); } /** Loading @@ -938,14 +941,14 @@ public final class NotificationChannel implements Parcelable { */ @SystemApi public void populateFromXml(XmlPullParser parser) { populateFromXml(XmlUtils.makeTyped(parser), false, null); populateFromXml(XmlUtils.makeTyped(parser), false, true, null); } /** * If {@param forRestore} is true, {@param Context} MUST be non-null. */ private void populateFromXml(TypedXmlPullParser parser, boolean forRestore, @Nullable Context context) { boolean pkgInstalled, @Nullable Context context) { Preconditions.checkArgument(!forRestore || context != null, "forRestore is true but got null context"); Loading @@ -956,7 +959,8 @@ public final class NotificationChannel implements Parcelable { setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY)); Uri sound = safeUri(parser, ATT_SOUND); setSound(forRestore ? restoreSoundUri(context, sound) : sound, safeAudioAttributes(parser)); setSound(forRestore ? restoreSoundUri(context, sound, pkgInstalled) : sound, safeAudioAttributes(parser)); enableLights(safeBool(parser, ATT_LIGHTS, false)); setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR)); Loading @@ -978,8 +982,58 @@ public final class NotificationChannel implements Parcelable { setImportantConversation(safeBool(parser, ATT_IMP_CONVERSATION, false)); } /** * Returns whether the sound for this channel was successfully restored * from backup. * @return false if the sound was not restored successfully. true otherwise (default value) * @hide */ public boolean isSoundRestored() { return mSoundRestored; } @Nullable private Uri getCanonicalizedSoundUri(ContentResolver contentResolver, @NonNull Uri uri) { if (Settings.System.DEFAULT_NOTIFICATION_URI.equals(uri)) { return uri; } if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme())) { try { contentResolver.getResourceId(uri); return uri; } catch (FileNotFoundException e) { return null; } } if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { return uri; } return contentResolver.canonicalize(uri); } @Nullable private Uri restoreSoundUri(Context context, @Nullable Uri uri) { private Uri getUncanonicalizedSoundUri(ContentResolver contentResolver, @NonNull Uri uri) { if (Settings.System.DEFAULT_NOTIFICATION_URI.equals(uri) || ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme()) || ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { return uri; } return contentResolver.uncanonicalize(uri); } /** * Restore/validate sound Uri from backup * @param context The Context * @param uri The sound Uri to restore * @param pkgInstalled If the parent package is installed * @return restored and validated Uri * @hide */ @Nullable public Uri restoreSoundUri(Context context, @Nullable Uri uri, boolean pkgInstalled) { if (uri == null || Uri.EMPTY.equals(uri)) { return null; } Loading @@ -991,12 +1045,22 @@ public final class NotificationChannel implements Parcelable { // 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); Uri canonicalizedUri = getCanonicalizedSoundUri(contentResolver, uri); if (canonicalizedUri == null) { // We got a null because the uri in the backup does not exist here, so we return default // Uri failed to restore with package installed if (!mSoundRestored && pkgInstalled) { mSoundRestored = true; // We got a null because the uri in the backup does not exist here, so we return // default return Settings.System.DEFAULT_NOTIFICATION_URI; } else { // Flag as unrestored and try again later (on package install) mSoundRestored = false; return uri; } } return contentResolver.uncanonicalize(canonicalizedUri); mSoundRestored = true; return getUncanonicalizedSoundUri(contentResolver, canonicalizedUri); } /** Loading @@ -1019,7 +1083,7 @@ public final class NotificationChannel implements Parcelable { if (sound == null || Uri.EMPTY.equals(sound)) { return null; } Uri canonicalSound = context.getContentResolver().canonicalize(sound); Uri canonicalSound = getCanonicalizedSoundUri(context.getContentResolver(), sound); if (canonicalSound == null) { // The content provider does not support canonical uris so we backup the default return Settings.System.DEFAULT_NOTIFICATION_URI; Loading services/core/java/com/android/server/notification/PreferencesHelper.java +19 −1 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.metrics.LogMaker; import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.UserHandle; Loading @@ -60,6 +61,7 @@ import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IntArray; import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; Loading Loading @@ -387,7 +389,8 @@ public class PreferencesHelper implements RankingConfig { NotificationChannel channel = new NotificationChannel( id, channelName, channelImportance); if (forRestore) { channel.populateFromXmlForRestore(parser, mContext); final boolean pkgInstalled = r.uid != UNKNOWN_UID; channel.populateFromXmlForRestore(parser, pkgInstalled, mContext); } else { channel.populateFromXml(parser); } Loading Loading @@ -2412,6 +2415,21 @@ public class PreferencesHelper implements RankingConfig { mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, changeUserId)); synchronized (mPackagePreferences) { mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r); // Try to restore any unrestored sound resources for (NotificationChannel channel : r.channels.values()) { if (!channel.isSoundRestored()) { Uri uri = channel.getSound(); Uri restoredUri = channel.restoreSoundUri(mContext, uri, true); if (Settings.System.DEFAULT_NOTIFICATION_URI.equals( restoredUri)) { Log.w(TAG, "Could not restore sound: " + uri + " for channel: " + channel); } channel.setSound(restoredUri, channel.getAudioAttributes()); } } } if (r.migrateToPm) { try { Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +1 −2 Original line number Diff line number Diff line Loading @@ -40,7 +40,6 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MAX; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; Loading Loading @@ -5312,7 +5311,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); NotificationChannel restored = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT); restored.populateFromXmlForRestore(parser, getContext()); restored.populateFromXmlForRestore(parser, true, getContext()); assertNull(restored.getSound()); } Loading services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +127 −0 Original line number Diff line number Diff line Loading @@ -140,6 +140,7 @@ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; Loading Loading @@ -170,6 +171,12 @@ public class PreferencesHelperTest extends UiServiceTestCase { Uri.parse("content://" + TEST_AUTHORITY + "/internal/audio/media/10?title=Test&canonical=1"); private static final Uri ANDROID_RES_SOUND_URI = Uri.parse("android.resource://" + TEST_AUTHORITY + "/raw/test"); private static final Uri FILE_SOUND_URI = Uri.parse("file://" + TEST_AUTHORITY + "/product/media/test.ogg"); @Mock PermissionHelper mPermissionHelper; @Mock RankingHandler mHandler; @Mock PackageManager mPm; Loading Loading @@ -1338,6 +1345,57 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound()); } /** * Test sound Uri restore retry behavior when channel is restored before package * and then package is installed. */ @Test public void testRestoreXml_withNonExistentCanonicalizedSoundUriAndMissingPackage() throws Exception { // canonicalization returns CANONICAL_SOUND_URI for getSoundForBackup (backup part) doReturn(CANONICAL_SOUND_URI) .when(mTestIContentProvider).canonicalize(any(), eq(SOUND_URI)); NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(SOUND_URI, mAudioAttributes); mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, USER_SYSTEM, channel.getId()); // canonicalization / uncanonicalization returns null for the restore part doReturn(null) .when(mTestIContentProvider).canonicalize(any(), eq(CANONICAL_SOUND_URI)); doReturn(null) .when(mTestIContentProvider).uncanonicalize(any(), any()); // simulate package not installed when(mPm.getPackageUidAsUser(PKG_N_MR1, USER_SYSTEM)).thenReturn(UNKNOWN_UID); when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenThrow( new PackageManager.NameNotFoundException()); loadStreamXml(baos, true, USER_SYSTEM); // 1st restore pass fails NotificationChannel actualChannel = mHelper.getNotificationChannel( PKG_N_MR1, UNKNOWN_UID, channel.getId(), false); // sound is CANONICAL_SOUND_URI, unchanged from backup assertEquals(CANONICAL_SOUND_URI, actualChannel.getSound()); // sound is flagged as not restored assertFalse(actualChannel.isSoundRestored()); // package is "installed" when(mPm.getPackageUidAsUser(PKG_N_MR1, USER_SYSTEM)).thenReturn(UID_N_MR1); // Trigger 2nd restore pass mHelper.onPackagesChanged(false, USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{UID_N_MR1}); // sound is flagged as restored and set to default URI assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound()); assertTrue(actualChannel.isSoundRestored()); } /** * Although we don't make backups with uncanonicalized uris anymore, we used to, so we have to Loading @@ -1363,7 +1421,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { backupWithUncanonicalizedSoundUri.getBytes(), true, USER_SYSTEM); NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, id, false); assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound()); assertTrue(actualChannel.isSoundRestored()); } @Test Loading @@ -1388,6 +1448,73 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertEquals(null, actualChannel.getSound()); } @Test public void testBackupRestoreXml_withAndroidResourceSoundUri() throws Exception { // Mock ContentResolver.getResourceId: // throw exception on restore 1st pass => simulate app not installed yet // then return a valid resource on package update => sim. app installed ContentResolver contentResolver = mock(ContentResolver.class); when(mContext.getContentResolver()).thenReturn(contentResolver); ContentResolver.OpenResourceIdResult resId = mock( ContentResolver.OpenResourceIdResult.class); when(contentResolver.getResourceId(ANDROID_RES_SOUND_URI)).thenReturn(resId).thenThrow( new FileNotFoundException("")).thenReturn(resId); mHelper = new PreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(ANDROID_RES_SOUND_URI, mAudioAttributes); mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, USER_SYSTEM, channel.getId()); // simulate package not installed when(mPm.getPackageUidAsUser(PKG_N_MR1, USER_SYSTEM)).thenReturn(UNKNOWN_UID); when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenThrow( new PackageManager.NameNotFoundException()); loadStreamXml(baos, true, USER_SYSTEM); NotificationChannel actualChannel = mHelper.getNotificationChannel( PKG_N_MR1, UNKNOWN_UID, channel.getId(), false); // sound is ANDROID_RES_SOUND_URI, unchanged from backup assertEquals(ANDROID_RES_SOUND_URI, actualChannel.getSound()); // sound is flagged as not restored assertFalse(actualChannel.isSoundRestored()); // package is "installed" when(mPm.getPackageUidAsUser(PKG_N_MR1, USER_SYSTEM)).thenReturn(UID_N_MR1); // Trigger 2nd restore pass mHelper.onPackagesChanged(false, USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{UID_N_MR1}); // sound is flagged as restored assertEquals(ANDROID_RES_SOUND_URI, actualChannel.getSound()); assertTrue(actualChannel.isSoundRestored()); } @Test public void testBackupRestoreXml_withFileResourceSoundUri() throws Exception { NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(FILE_SOUND_URI, mAudioAttributes); mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, USER_SYSTEM, channel.getId()); loadStreamXml(baos, true, USER_SYSTEM); NotificationChannel actualChannel = mHelper.getNotificationChannel( PKG_N_MR1, UID_N_MR1, channel.getId(), false); // sound is FILE_SOUND_URI, unchanged from backup assertEquals(FILE_SOUND_URI, actualChannel.getSound()); // sound is flagged as restored assertTrue(actualChannel.isSoundRestored()); } @Test public void testChannelXml_backup() throws Exception { NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye"); Loading Loading
core/java/android/app/NotificationChannel.java +75 −11 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; Loading Loading @@ -246,6 +247,7 @@ public final class NotificationChannel implements Parcelable { private boolean mBypassDnd; private int mLockscreenVisibility = DEFAULT_VISIBILITY; private Uri mSound = Settings.System.DEFAULT_NOTIFICATION_URI; private boolean mSoundRestored = false; private boolean mLights; private int mLightColor = DEFAULT_LIGHT_COLOR; private long[] mVibration; Loading Loading @@ -929,8 +931,9 @@ public final class NotificationChannel implements Parcelable { /** * @hide */ public void populateFromXmlForRestore(XmlPullParser parser, Context context) { populateFromXml(XmlUtils.makeTyped(parser), true, context); public void populateFromXmlForRestore(XmlPullParser parser, boolean pkgInstalled, Context context) { populateFromXml(XmlUtils.makeTyped(parser), true, pkgInstalled, context); } /** Loading @@ -938,14 +941,14 @@ public final class NotificationChannel implements Parcelable { */ @SystemApi public void populateFromXml(XmlPullParser parser) { populateFromXml(XmlUtils.makeTyped(parser), false, null); populateFromXml(XmlUtils.makeTyped(parser), false, true, null); } /** * If {@param forRestore} is true, {@param Context} MUST be non-null. */ private void populateFromXml(TypedXmlPullParser parser, boolean forRestore, @Nullable Context context) { boolean pkgInstalled, @Nullable Context context) { Preconditions.checkArgument(!forRestore || context != null, "forRestore is true but got null context"); Loading @@ -956,7 +959,8 @@ public final class NotificationChannel implements Parcelable { setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY)); Uri sound = safeUri(parser, ATT_SOUND); setSound(forRestore ? restoreSoundUri(context, sound) : sound, safeAudioAttributes(parser)); setSound(forRestore ? restoreSoundUri(context, sound, pkgInstalled) : sound, safeAudioAttributes(parser)); enableLights(safeBool(parser, ATT_LIGHTS, false)); setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR)); Loading @@ -978,8 +982,58 @@ public final class NotificationChannel implements Parcelable { setImportantConversation(safeBool(parser, ATT_IMP_CONVERSATION, false)); } /** * Returns whether the sound for this channel was successfully restored * from backup. * @return false if the sound was not restored successfully. true otherwise (default value) * @hide */ public boolean isSoundRestored() { return mSoundRestored; } @Nullable private Uri getCanonicalizedSoundUri(ContentResolver contentResolver, @NonNull Uri uri) { if (Settings.System.DEFAULT_NOTIFICATION_URI.equals(uri)) { return uri; } if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme())) { try { contentResolver.getResourceId(uri); return uri; } catch (FileNotFoundException e) { return null; } } if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { return uri; } return contentResolver.canonicalize(uri); } @Nullable private Uri restoreSoundUri(Context context, @Nullable Uri uri) { private Uri getUncanonicalizedSoundUri(ContentResolver contentResolver, @NonNull Uri uri) { if (Settings.System.DEFAULT_NOTIFICATION_URI.equals(uri) || ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme()) || ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { return uri; } return contentResolver.uncanonicalize(uri); } /** * Restore/validate sound Uri from backup * @param context The Context * @param uri The sound Uri to restore * @param pkgInstalled If the parent package is installed * @return restored and validated Uri * @hide */ @Nullable public Uri restoreSoundUri(Context context, @Nullable Uri uri, boolean pkgInstalled) { if (uri == null || Uri.EMPTY.equals(uri)) { return null; } Loading @@ -991,12 +1045,22 @@ public final class NotificationChannel implements Parcelable { // 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); Uri canonicalizedUri = getCanonicalizedSoundUri(contentResolver, uri); if (canonicalizedUri == null) { // We got a null because the uri in the backup does not exist here, so we return default // Uri failed to restore with package installed if (!mSoundRestored && pkgInstalled) { mSoundRestored = true; // We got a null because the uri in the backup does not exist here, so we return // default return Settings.System.DEFAULT_NOTIFICATION_URI; } else { // Flag as unrestored and try again later (on package install) mSoundRestored = false; return uri; } } return contentResolver.uncanonicalize(canonicalizedUri); mSoundRestored = true; return getUncanonicalizedSoundUri(contentResolver, canonicalizedUri); } /** Loading @@ -1019,7 +1083,7 @@ public final class NotificationChannel implements Parcelable { if (sound == null || Uri.EMPTY.equals(sound)) { return null; } Uri canonicalSound = context.getContentResolver().canonicalize(sound); Uri canonicalSound = getCanonicalizedSoundUri(context.getContentResolver(), sound); if (canonicalSound == null) { // The content provider does not support canonical uris so we backup the default return Settings.System.DEFAULT_NOTIFICATION_URI; Loading
services/core/java/com/android/server/notification/PreferencesHelper.java +19 −1 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.metrics.LogMaker; import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.UserHandle; Loading @@ -60,6 +61,7 @@ import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IntArray; import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; Loading Loading @@ -387,7 +389,8 @@ public class PreferencesHelper implements RankingConfig { NotificationChannel channel = new NotificationChannel( id, channelName, channelImportance); if (forRestore) { channel.populateFromXmlForRestore(parser, mContext); final boolean pkgInstalled = r.uid != UNKNOWN_UID; channel.populateFromXmlForRestore(parser, pkgInstalled, mContext); } else { channel.populateFromXml(parser); } Loading Loading @@ -2412,6 +2415,21 @@ public class PreferencesHelper implements RankingConfig { mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, changeUserId)); synchronized (mPackagePreferences) { mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r); // Try to restore any unrestored sound resources for (NotificationChannel channel : r.channels.values()) { if (!channel.isSoundRestored()) { Uri uri = channel.getSound(); Uri restoredUri = channel.restoreSoundUri(mContext, uri, true); if (Settings.System.DEFAULT_NOTIFICATION_URI.equals( restoredUri)) { Log.w(TAG, "Could not restore sound: " + uri + " for channel: " + channel); } channel.setSound(restoredUri, channel.getAudioAttributes()); } } } if (r.migrateToPm) { try { Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +1 −2 Original line number Diff line number Diff line Loading @@ -40,7 +40,6 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MAX; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; Loading Loading @@ -5312,7 +5311,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); NotificationChannel restored = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT); restored.populateFromXmlForRestore(parser, getContext()); restored.populateFromXmlForRestore(parser, true, getContext()); assertNull(restored.getSound()); } Loading
services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +127 −0 Original line number Diff line number Diff line Loading @@ -140,6 +140,7 @@ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; Loading Loading @@ -170,6 +171,12 @@ public class PreferencesHelperTest extends UiServiceTestCase { Uri.parse("content://" + TEST_AUTHORITY + "/internal/audio/media/10?title=Test&canonical=1"); private static final Uri ANDROID_RES_SOUND_URI = Uri.parse("android.resource://" + TEST_AUTHORITY + "/raw/test"); private static final Uri FILE_SOUND_URI = Uri.parse("file://" + TEST_AUTHORITY + "/product/media/test.ogg"); @Mock PermissionHelper mPermissionHelper; @Mock RankingHandler mHandler; @Mock PackageManager mPm; Loading Loading @@ -1338,6 +1345,57 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound()); } /** * Test sound Uri restore retry behavior when channel is restored before package * and then package is installed. */ @Test public void testRestoreXml_withNonExistentCanonicalizedSoundUriAndMissingPackage() throws Exception { // canonicalization returns CANONICAL_SOUND_URI for getSoundForBackup (backup part) doReturn(CANONICAL_SOUND_URI) .when(mTestIContentProvider).canonicalize(any(), eq(SOUND_URI)); NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(SOUND_URI, mAudioAttributes); mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, USER_SYSTEM, channel.getId()); // canonicalization / uncanonicalization returns null for the restore part doReturn(null) .when(mTestIContentProvider).canonicalize(any(), eq(CANONICAL_SOUND_URI)); doReturn(null) .when(mTestIContentProvider).uncanonicalize(any(), any()); // simulate package not installed when(mPm.getPackageUidAsUser(PKG_N_MR1, USER_SYSTEM)).thenReturn(UNKNOWN_UID); when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenThrow( new PackageManager.NameNotFoundException()); loadStreamXml(baos, true, USER_SYSTEM); // 1st restore pass fails NotificationChannel actualChannel = mHelper.getNotificationChannel( PKG_N_MR1, UNKNOWN_UID, channel.getId(), false); // sound is CANONICAL_SOUND_URI, unchanged from backup assertEquals(CANONICAL_SOUND_URI, actualChannel.getSound()); // sound is flagged as not restored assertFalse(actualChannel.isSoundRestored()); // package is "installed" when(mPm.getPackageUidAsUser(PKG_N_MR1, USER_SYSTEM)).thenReturn(UID_N_MR1); // Trigger 2nd restore pass mHelper.onPackagesChanged(false, USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{UID_N_MR1}); // sound is flagged as restored and set to default URI assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound()); assertTrue(actualChannel.isSoundRestored()); } /** * Although we don't make backups with uncanonicalized uris anymore, we used to, so we have to Loading @@ -1363,7 +1421,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { backupWithUncanonicalizedSoundUri.getBytes(), true, USER_SYSTEM); NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, id, false); assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound()); assertTrue(actualChannel.isSoundRestored()); } @Test Loading @@ -1388,6 +1448,73 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertEquals(null, actualChannel.getSound()); } @Test public void testBackupRestoreXml_withAndroidResourceSoundUri() throws Exception { // Mock ContentResolver.getResourceId: // throw exception on restore 1st pass => simulate app not installed yet // then return a valid resource on package update => sim. app installed ContentResolver contentResolver = mock(ContentResolver.class); when(mContext.getContentResolver()).thenReturn(contentResolver); ContentResolver.OpenResourceIdResult resId = mock( ContentResolver.OpenResourceIdResult.class); when(contentResolver.getResourceId(ANDROID_RES_SOUND_URI)).thenReturn(resId).thenThrow( new FileNotFoundException("")).thenReturn(resId); mHelper = new PreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(ANDROID_RES_SOUND_URI, mAudioAttributes); mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, USER_SYSTEM, channel.getId()); // simulate package not installed when(mPm.getPackageUidAsUser(PKG_N_MR1, USER_SYSTEM)).thenReturn(UNKNOWN_UID); when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenThrow( new PackageManager.NameNotFoundException()); loadStreamXml(baos, true, USER_SYSTEM); NotificationChannel actualChannel = mHelper.getNotificationChannel( PKG_N_MR1, UNKNOWN_UID, channel.getId(), false); // sound is ANDROID_RES_SOUND_URI, unchanged from backup assertEquals(ANDROID_RES_SOUND_URI, actualChannel.getSound()); // sound is flagged as not restored assertFalse(actualChannel.isSoundRestored()); // package is "installed" when(mPm.getPackageUidAsUser(PKG_N_MR1, USER_SYSTEM)).thenReturn(UID_N_MR1); // Trigger 2nd restore pass mHelper.onPackagesChanged(false, USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{UID_N_MR1}); // sound is flagged as restored assertEquals(ANDROID_RES_SOUND_URI, actualChannel.getSound()); assertTrue(actualChannel.isSoundRestored()); } @Test public void testBackupRestoreXml_withFileResourceSoundUri() throws Exception { NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(FILE_SOUND_URI, mAudioAttributes); mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, USER_SYSTEM, channel.getId()); loadStreamXml(baos, true, USER_SYSTEM); NotificationChannel actualChannel = mHelper.getNotificationChannel( PKG_N_MR1, UID_N_MR1, channel.getId(), false); // sound is FILE_SOUND_URI, unchanged from backup assertEquals(FILE_SOUND_URI, actualChannel.getSound()); // sound is flagged as restored assertTrue(actualChannel.isSoundRestored()); } @Test public void testChannelXml_backup() throws Exception { NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye"); Loading