Allow notifications to be autocanceled at a time.

Test: cts
Change-Id: I24ebcab2c0fa2028ec5d2984dc3b7ec19391b590
This commit is contained in:
Julia Reynolds
2016-11-28 14:29:25 -05:00
parent 573c653708
commit 2a128746b3
9 changed files with 100 additions and 5 deletions

View File

@@ -5016,6 +5016,7 @@ package android.app {
method public android.graphics.drawable.Icon getLargeIcon();
method public android.graphics.drawable.Icon getSmallIcon();
method public java.lang.String getSortKey();
method public long getTimeout();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
field public static final java.lang.String CATEGORY_ALARM = "alarm";
@@ -5244,6 +5245,7 @@ package android.app {
method public android.app.Notification.Builder setSubText(java.lang.CharSequence);
method public android.app.Notification.Builder setTicker(java.lang.CharSequence);
method public deprecated android.app.Notification.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
method public android.app.Notification.Builder setTimeout(long);
method public android.app.Notification.Builder setUsesChronometer(boolean);
method public android.app.Notification.Builder setVibrate(long[]);
method public android.app.Notification.Builder setVisibility(int);
@@ -35873,9 +35875,9 @@ package android.service.notification {
field public static final int REASON_PACKAGE_SUSPENDED = 14; // 0xe
field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf
field public static final int REASON_SNOOZED = 18; // 0x12
field public static final int REASON_TIMEOUT = 19; // 0x13
field public static final int REASON_UNAUTOBUNDLED = 16; // 0x10
field public static final int REASON_USER_STOPPED = 6; // 0x6
field public static final int REASON_USER_SWITCH = 19; // 0x13
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
field public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1; // 0x1
field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 2; // 0x2

View File

@@ -5174,6 +5174,7 @@ package android.app {
method public static java.lang.Class<? extends android.app.Notification.Style> getNotificationStyleClass(java.lang.String);
method public android.graphics.drawable.Icon getSmallIcon();
method public java.lang.String getSortKey();
method public long getTimeout();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
field public static final java.lang.String CATEGORY_ALARM = "alarm";
@@ -5404,6 +5405,7 @@ package android.app {
method public android.app.Notification.Builder setSubText(java.lang.CharSequence);
method public android.app.Notification.Builder setTicker(java.lang.CharSequence);
method public deprecated android.app.Notification.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
method public android.app.Notification.Builder setTimeout(long);
method public android.app.Notification.Builder setUsesChronometer(boolean);
method public android.app.Notification.Builder setVibrate(long[]);
method public android.app.Notification.Builder setVisibility(int);
@@ -38861,9 +38863,9 @@ package android.service.notification {
field public static final int REASON_PACKAGE_SUSPENDED = 14; // 0xe
field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf
field public static final int REASON_SNOOZED = 18; // 0x12
field public static final int REASON_TIMEOUT = 19; // 0x13
field public static final int REASON_UNAUTOBUNDLED = 16; // 0x10
field public static final int REASON_USER_STOPPED = 6; // 0x6
field public static final int REASON_USER_SWITCH = 19; // 0x13
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
field public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1; // 0x1
field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 2; // 0x2

View File

@@ -5026,6 +5026,7 @@ package android.app {
method public android.graphics.drawable.Icon getLargeIcon();
method public android.graphics.drawable.Icon getSmallIcon();
method public java.lang.String getSortKey();
method public long getTimeout();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
field public static final java.lang.String CATEGORY_ALARM = "alarm";
@@ -5254,6 +5255,7 @@ package android.app {
method public android.app.Notification.Builder setSubText(java.lang.CharSequence);
method public android.app.Notification.Builder setTicker(java.lang.CharSequence);
method public deprecated android.app.Notification.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
method public android.app.Notification.Builder setTimeout(long);
method public android.app.Notification.Builder setUsesChronometer(boolean);
method public android.app.Notification.Builder setVibrate(long[]);
method public android.app.Notification.Builder setVisibility(int);
@@ -35994,9 +35996,9 @@ package android.service.notification {
field public static final int REASON_PACKAGE_SUSPENDED = 14; // 0xe
field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf
field public static final int REASON_SNOOZED = 18; // 0x12
field public static final int REASON_TIMEOUT = 19; // 0x13
field public static final int REASON_UNAUTOBUNDLED = 16; // 0x10
field public static final int REASON_USER_STOPPED = 6; // 0x6
field public static final int REASON_USER_SWITCH = 19; // 0x13
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
field public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1; // 0x1
field public static final int SUPPRESSED_EFFECT_SCREEN_ON = 2; // 0x2

View File

@@ -1022,6 +1022,7 @@ public class Notification implements Parcelable
private Icon mLargeIcon;
private String mChannelId;
private long mTimeout;
/**
* Structure to encapsulate a named action that can be shown as part of this notification.
@@ -1766,6 +1767,7 @@ public class Notification implements Parcelable
if (parcel.readInt() != 0) {
mChannelId = parcel.readString();
}
mTimeout = parcel.readLong();
}
@Override
@@ -1872,6 +1874,7 @@ public class Notification implements Parcelable
that.color = this.color;
that.mChannelId = this.mChannelId;
that.mTimeout = this.mTimeout;
if (!heavy) {
that.lightenPayload(); // will clean out extras
@@ -2128,6 +2131,7 @@ public class Notification implements Parcelable
} else {
parcel.writeInt(0);
}
parcel.writeLong(mTimeout);
}
/**
@@ -2324,6 +2328,13 @@ public class Notification implements Parcelable
return mChannelId;
}
/**
* Returns the time at which this notification should be canceled, if it's not canceled already.
*/
public long getTimeout() {
return mTimeout;
}
/**
* The small icon representing this notification in the status bar and content view.
*
@@ -2531,6 +2542,15 @@ public class Notification implements Parcelable
return this;
}
/**
* Specifies the time at which this notification should be canceled, if it is not already
* canceled.
*/
public Builder setTimeout(long when) {
mN.mTimeout = when;
return this;
}
/**
* Add a timestamp pertaining to the notification (usually the time the event occurred).
*

View File

@@ -183,8 +183,8 @@ public abstract class NotificationListenerService extends Service {
public static final int REASON_CHANNEL_BANNED = 17;
/** Notification was snoozed. */
public static final int REASON_SNOOZED = 18;
/** Notification no longer visible because of user switch */
public static final int REASON_USER_SWITCH = 19;
/** Notification was canceled due to timeout */
public static final int REASON_TIMEOUT = 19;
/**
* The full trim of the StatusBarNotification including all its features.

View File

@@ -443,6 +443,7 @@
<protected-broadcast android:name="com.android.server.telecom.intent.action.CALLS_ADD_ENTRY" />
<protected-broadcast android:name="com.android.settings.location.MODE_CHANGING" />
<protected-broadcast android:name="NotificationManagerService.TIMEOUT" />
<protected-broadcast android:name="ScheduleConditionProvider.EVALUATE" />
<protected-broadcast android:name="EventConditionProvider.EVALUATE" />
<protected-broadcast android:name="SnoozeHelper.EVALUATE" />

View File

@@ -32,6 +32,7 @@ import static android.service.notification.NotificationListenerService.REASON_PA
import static android.service.notification.NotificationListenerService.REASON_PACKAGE_SUSPENDED;
import static android.service.notification.NotificationListenerService.REASON_PROFILE_TURNED_OFF;
import static android.service.notification.NotificationListenerService.REASON_SNOOZED;
import static android.service.notification.NotificationListenerService.REASON_TIMEOUT;
import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
@@ -50,6 +51,7 @@ import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AutomaticZenRule;
@@ -165,6 +167,7 @@ import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -229,6 +232,12 @@ public class NotificationManagerService extends SystemService {
private static final long DELAY_FOR_ASSISTANT_TIME = 100;
private static final String ACTION_NOTIFICATION_TIMEOUT =
NotificationManagerService.class.getSimpleName() + ".TIMEOUT";
private static final int REQUEST_CODE_TIMEOUT = 1;
private static final String SCHEME_TIMEOUT = "timeout";
private static final String EXTRA_KEY = "key";
private IActivityManager mAm;
private IPackageManager mPackageManager;
private PackageManager mPackageManagerClient;
@@ -237,6 +246,7 @@ public class NotificationManagerService extends SystemService {
@Nullable StatusBarManagerInternal mStatusBar;
Vibrator mVibrator;
private WindowManagerInternal mWindowManagerInternal;
private AlarmManager mAlarmManager;
final IBinder mForegroundToken = new Binder();
private Handler mHandler;
@@ -682,6 +692,29 @@ public class NotificationManagerService extends SystemService {
updateLightsLocked();
}
private final BroadcastReceiver mNotificationTimeoutReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action == null) {
return;
}
if (ACTION_NOTIFICATION_TIMEOUT.equals(action)) {
final NotificationRecord record;
synchronized (mNotificationLock) {
record = findNotificationByKeyLocked(intent.getStringExtra(EXTRA_KEY));
}
if (record != null) {
cancelNotification(record.sbn.getUid(), record.sbn.getInitialPid(),
record.sbn.getPackageName(), record.sbn.getTag(),
record.sbn.getId(), 0,
Notification.FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
REASON_TIMEOUT, null);
}
}
}
};
private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -966,6 +999,7 @@ public class NotificationManagerService extends SystemService {
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
mHandler = new WorkerHandler(looper);
mRankingThread.start();
@@ -1132,6 +1166,10 @@ public class NotificationManagerService extends SystemService {
getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
null);
IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter);
mSettingsObserver = new SettingsObserver(mHandler);
mArchive = new Archive(resources.getInteger(
@@ -3011,6 +3049,7 @@ public class NotificationManagerService extends SystemService {
public void run() {
synchronized (mNotificationLock) {
mEnqueuedNotifications.add(r);
scheduleTimeoutLocked(r);
if (mSnoozeHelper.isSnoozed(userId, r.sbn.getPackageName(), r.getKey())) {
// TODO: log to event log
@@ -3240,6 +3279,22 @@ public class NotificationManagerService extends SystemService {
}
}
@VisibleForTesting
void scheduleTimeoutLocked(NotificationRecord record) {
if (record.getNotification().getTimeout() > System.currentTimeMillis()) {
final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
REQUEST_CODE_TIMEOUT,
new Intent(ACTION_NOTIFICATION_TIMEOUT)
.setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
.appendPath(record.getKey()).build())
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
.putExtra(EXTRA_KEY, record.getKey()),
PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP, record.getNotification().getTimeout(), pi);
}
}
@VisibleForTesting
void buzzBeepBlinkLocked(NotificationRecord record) {
boolean buzz = false;

View File

@@ -41,6 +41,7 @@ import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import android.util.TimeUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.EventLogTags;
@@ -318,6 +319,7 @@ public final class NotificationRecord {
pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate));
pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d",
notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
pw.println(prefix + " timeout=" + TimeUtils.formatForLogging(notification.getTimeout()));
if (notification.actions != null && notification.actions.length > 0) {
pw.println(prefix + " actions={");
final int N = notification.actions.length;

View File

@@ -424,6 +424,17 @@ public class NotificationTestList extends TestActivity
mNM.notify("secret", 7012, n);
}
},
new Test("1 minute timeout") {
public void run()
{
Notification n = new Notification.Builder(NotificationTestList.this)
.setSmallIcon(R.drawable.icon2)
.setContentTitle("timeout in a minute")
.setTimeout(System.currentTimeMillis() + (1000 * 60))
.build();
mNM.notify("timeout_min", 7013, n);
}
},
new Test("Off") {
public void run() {
PowerManager pm = (PowerManager)NotificationTestList.this.getSystemService(Context.POWER_SERVICE);