diff options
| author | Bernardo Rufino <brufino@google.com> | 2017-10-03 13:55:10 +0100 |
|---|---|---|
| committer | Michal Karpinski <mkarpinski@google.com> | 2017-10-11 15:22:32 +0100 |
| commit | 2d7a4a3f674d62adfb2825b738f2e87ec246194d (patch) | |
| tree | 1a805b16dab25d1fbe5fb74e465a4fb59fdbb8f5 /core/java/android/app/NotificationChannel.java | |
| parent | 43aca9cdc8fcaaafd98341aa87549630b2264367 (diff) | |
Canonicalize notification channel sounds for backup
Canonicalize for backup and canonicalize and uncanonicalize for restore
(see comment).
Test: Set custom notification sound, make backup, remove notification
sound from device (from Ringtones and make sure to update media content
provider), restore => Observe default instead of random number. Do the
same without removing the sound and observe restores successfully.
Test: runtest systemui-notification
Bug: 66444697
(cherry picked from commit c27bb6ad34fd59161f6460b692ba72c7ede789b6)
Change-Id: I32c186d0d7479b01f6cc67cce9bc5cb66264a064
Diffstat (limited to 'core/java/android/app/NotificationChannel.java')
| -rw-r--r-- | core/java/android/app/NotificationChannel.java | 84 |
1 files changed, 81 insertions, 3 deletions
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index d6e36914ac6c..556acdcfff81 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -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; @@ -26,6 +29,8 @@ import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.text.TextUtils; +import com.android.internal.util.Preconditions; + import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; @@ -565,14 +570,35 @@ public final class NotificationChannel implements Parcelable { /** * @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)); @@ -584,11 +610,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) { @@ -609,8 +686,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())); |
