From 780861f2452b519c128289dac836e5a756100e1d Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Mon, 20 Mar 2017 14:38:04 -0600 Subject: More RecoverableSecurityException docs. Mention convenience methods in class docs, and require a valid NotificationChannel to be provided to match O best-practices. Also mention that notifyChange() should be triggered when the action is finished. Test: builds, boots Bug: 34676491 Change-Id: I0e6c6d43a93cbce1a5de02621290cc2ff9423274 --- api/current.txt | 2 +- api/removed.txt | 4 ++ api/system-current.txt | 2 +- api/system-removed.txt | 4 ++ api/test-current.txt | 2 +- api/test-removed.txt | 4 ++ .../android/app/RecoverableSecurityException.java | 57 ++++++++++++++++------ 7 files changed, 58 insertions(+), 17 deletions(-) diff --git a/api/current.txt b/api/current.txt index aab29be21d0f..5ea296ca6c3e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5705,7 +5705,7 @@ package android.app { method public android.app.RemoteAction getUserAction(); method public java.lang.CharSequence getUserMessage(); method public void showAsDialog(android.app.Activity); - method public void showAsNotification(android.content.Context); + method public void showAsNotification(android.content.Context, java.lang.String); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; } diff --git a/api/removed.txt b/api/removed.txt index 148f3f1e3281..04c9c35428b3 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -4,6 +4,10 @@ package android.app { method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent); } + public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable { + method public deprecated void showAsNotification(android.content.Context); + } + } package android.app.admin { diff --git a/api/system-current.txt b/api/system-current.txt index 5e6717cbece4..90d7b4fa1244 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5908,7 +5908,7 @@ package android.app { method public android.app.RemoteAction getUserAction(); method public java.lang.CharSequence getUserMessage(); method public void showAsDialog(android.app.Activity); - method public void showAsNotification(android.content.Context); + method public void showAsNotification(android.content.Context, java.lang.String); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; } diff --git a/api/system-removed.txt b/api/system-removed.txt index bd535d2be513..640dc81877a1 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -4,6 +4,10 @@ package android.app { method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent); } + public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable { + method public deprecated void showAsNotification(android.content.Context); + } + } package android.app.admin { diff --git a/api/test-current.txt b/api/test-current.txt index f91bbb9a045b..abcf2d47d188 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -5716,7 +5716,7 @@ package android.app { method public android.app.RemoteAction getUserAction(); method public java.lang.CharSequence getUserMessage(); method public void showAsDialog(android.app.Activity); - method public void showAsNotification(android.content.Context); + method public void showAsNotification(android.content.Context, java.lang.String); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; } diff --git a/api/test-removed.txt b/api/test-removed.txt index 148f3f1e3281..04c9c35428b3 100644 --- a/api/test-removed.txt +++ b/api/test-removed.txt @@ -4,6 +4,10 @@ package android.app { method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent); } + public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable { + method public deprecated void showAsNotification(android.content.Context); + } + } package android.app.admin { diff --git a/core/java/android/app/RecoverableSecurityException.java b/core/java/android/app/RecoverableSecurityException.java index 540d1cda1edd..8612f186ade4 100644 --- a/core/java/android/app/RecoverableSecurityException.java +++ b/core/java/android/app/RecoverableSecurityException.java @@ -16,8 +16,9 @@ package android.app; +import android.content.ContentProvider; +import android.content.ContentResolver; import android.content.Context; -import android.content.res.Resources; import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.Parcel; @@ -31,7 +32,15 @@ import com.android.internal.util.Preconditions; *

* This exception is only appropriate where there is a concrete action the user * can take to recover and make forward progress, such as confirming or entering - * authentication credentials. + * authentication credentials, or granting access. + *

+ * If the receiving app is actively involved with the user, it should present + * the contained recovery details to help the user make forward progress. The + * {@link #showAsDialog(Activity)} and + * {@link #showAsNotification(Context, String)} methods are provided as a + * convenience, but receiving apps are encouraged to use + * {@link #getUserMessage()} and {@link #getUserAction()} to integrate in a more + * natural way if relevant. *

* Note: legacy code that receives this exception may treat it as a general * {@link SecurityException}, and thus there is no guarantee that the messages @@ -66,7 +75,10 @@ public final class RecoverableSecurityException extends SecurityException implem * {@link Activity#setResult(int)} before finishing to * communicate the final status of the recovery. For example, * apps that observe {@link Activity#RESULT_OK} may choose to - * immediately retry their operation. + * immediately retry their operation. If this exception was + * thrown from a {@link ContentProvider}, you should also send + * any relevant {@link ContentResolver#notifyChange} events to + * trigger reloading of data. */ public RecoverableSecurityException(Throwable cause, CharSequence userMessage, RemoteAction userAction) { @@ -101,6 +113,20 @@ public final class RecoverableSecurityException extends SecurityException implem return mUserAction; } + /** @removed */ + @Deprecated + public void showAsNotification(Context context) { + final NotificationManager nm = context.getSystemService(NotificationManager.class); + + // Create a channel per-sender, since we don't want one poorly behaved + // remote app to cause all of our notifications to be blocked + final String channelId = TAG + "_" + mUserAction.getActionIntent().getCreatorUid(); + nm.createNotificationChannel(new NotificationChannel(channelId, TAG, + NotificationManager.IMPORTANCE_DEFAULT)); + + showAsNotification(context, channelId); + } + /** * Convenience method that will show a very simple notification populated * with the details from this exception. @@ -114,23 +140,20 @@ public final class RecoverableSecurityException extends SecurityException implem *

* This method will only display the most recent exception from any single * remote UID; notifications from older exceptions will always be replaced. + * + * @param channelId the {@link NotificationChannel} to use, which must have + * been already created using + * {@link NotificationManager#createNotificationChannel}. */ - public void showAsNotification(Context context) { + public void showAsNotification(Context context, String channelId) { final NotificationManager nm = context.getSystemService(NotificationManager.class); - - // Create a channel per-sender, since we don't want one poorly behaved - // remote app to cause all of our notifications to be blocked - final String tag = TAG + "_" + mUserAction.getActionIntent().getCreatorUid(); - nm.createNotificationChannel(new NotificationChannel(tag, TAG, - NotificationManager.IMPORTANCE_DEFAULT)); - - final Notification.Builder builder = new Notification.Builder(context, tag) + final Notification.Builder builder = new Notification.Builder(context, channelId) .setSmallIcon(com.android.internal.R.drawable.ic_print_error) .setContentTitle(mUserAction.getTitle()) .setContentText(mUserMessage) .setContentIntent(mUserAction.getActionIntent()) .setCategory(Notification.CATEGORY_ERROR); - nm.notify(tag, 0, builder.build()); + nm.notify(TAG, mUserAction.getActionIntent().getCreatorUid(), builder.build()); } /** @@ -164,7 +187,13 @@ public final class RecoverableSecurityException extends SecurityException implem ft.commitAllowingStateLoss(); } - /** {@hide} */ + /** + * Implementation detail for + * {@link RecoverableSecurityException#showAsDialog(Activity)}; needs to + * remain static to be recreated across orientation changes. + * + * @hide + */ public static class LocalDialog extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { -- cgit v1.2.3-59-g8ed1b