DO NOT MERGE: Revert "Force FGS notifications to show for a minimum time"
am: 7c6df8dad1
Change-Id: I30774ee4835a9be8617894e2b32698ed7944ae0f
This commit is contained in:
@@ -16,20 +16,14 @@
|
|||||||
|
|
||||||
package com.android.systemui;
|
package com.android.systemui;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
import android.util.ArraySet;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
|
||||||
import com.android.internal.statusbar.NotificationVisibility;
|
import com.android.internal.statusbar.NotificationVisibility;
|
||||||
import com.android.systemui.statusbar.NotificationLifetimeExtender;
|
|
||||||
import com.android.systemui.statusbar.notification.NotificationEntryListener;
|
import com.android.systemui.statusbar.notification.NotificationEntryListener;
|
||||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||||
@@ -72,9 +66,6 @@ public class ForegroundServiceNotificationListener {
|
|||||||
removeNotification(entry.notification);
|
removeNotification(entry.notification);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
notificationEntryManager.addNotificationLifetimeExtender(
|
|
||||||
new ForegroundServiceNotificationListener.ForegroundServiceLifetimeExtender());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -153,65 +144,4 @@ public class ForegroundServiceNotificationListener {
|
|||||||
},
|
},
|
||||||
true /* create if not found */);
|
true /* create if not found */);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Extends the lifetime of foreground notification services such that they show for at least
|
|
||||||
* five seconds
|
|
||||||
*/
|
|
||||||
public static class ForegroundServiceLifetimeExtender implements NotificationLifetimeExtender {
|
|
||||||
|
|
||||||
private static final String TAG = "FGSLifetimeExtender";
|
|
||||||
@VisibleForTesting
|
|
||||||
static final int MIN_FGS_TIME_MS = 5000;
|
|
||||||
|
|
||||||
private NotificationSafeToRemoveCallback mNotificationSafeToRemoveCallback;
|
|
||||||
private ArraySet<NotificationEntry> mManagedEntries = new ArraySet<>();
|
|
||||||
private Handler mHandler = new Handler(Looper.getMainLooper());
|
|
||||||
|
|
||||||
public ForegroundServiceLifetimeExtender() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCallback(@NonNull NotificationSafeToRemoveCallback callback) {
|
|
||||||
mNotificationSafeToRemoveCallback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
|
|
||||||
if ((entry.notification.getNotification().flags
|
|
||||||
& Notification.FLAG_FOREGROUND_SERVICE) == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
long currentTime = System.currentTimeMillis();
|
|
||||||
return currentTime - entry.notification.getPostTime() < MIN_FGS_TIME_MS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldExtendLifetimeForPendingNotification(
|
|
||||||
@NonNull NotificationEntry entry) {
|
|
||||||
return shouldExtendLifetime(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setShouldManageLifetime(
|
|
||||||
@NonNull NotificationEntry entry, boolean shouldManage) {
|
|
||||||
if (!shouldManage) {
|
|
||||||
mManagedEntries.remove(entry);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mManagedEntries.add(entry);
|
|
||||||
|
|
||||||
Runnable r = () -> {
|
|
||||||
if (mManagedEntries.contains(entry)) {
|
|
||||||
if (mNotificationSafeToRemoveCallback != null) {
|
|
||||||
mNotificationSafeToRemoveCallback.onSafeToRemove(entry.key);
|
|
||||||
}
|
|
||||||
mManagedEntries.remove(entry);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
mHandler.postDelayed(r, MIN_FGS_TIME_MS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,18 +26,6 @@ public interface NotificationLifetimeExtender {
|
|||||||
*/
|
*/
|
||||||
boolean shouldExtendLifetime(@NonNull NotificationEntry entry);
|
boolean shouldExtendLifetime(@NonNull NotificationEntry entry);
|
||||||
|
|
||||||
/**
|
|
||||||
* It's possible that a notification was canceled before it ever became visible. This callback
|
|
||||||
* gives lifetime extenders a chance to make sure it shows up. For example if a foreground
|
|
||||||
* service is canceled too quickly but we still want to make sure a FGS notification shows.
|
|
||||||
* @param pendingEntry the canceled (but pending) entry
|
|
||||||
* @return true if the notification lifetime should be extended
|
|
||||||
*/
|
|
||||||
default boolean shouldExtendLifetimeForPendingNotification(
|
|
||||||
@NonNull NotificationEntry pendingEntry) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether or not the lifetime should be managed by the extender. In practice, if
|
* Sets whether or not the lifetime should be managed by the extender. In practice, if
|
||||||
* shouldManage is true, this is where the extender starts managing the entry internally and is
|
* shouldManage is true, this is where the extender starts managing the entry internally and is
|
||||||
|
|||||||
@@ -281,25 +281,11 @@ public class NotificationEntryManager implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
final NotificationEntry entry = mNotificationData.get(key);
|
final NotificationEntry entry = mNotificationData.get(key);
|
||||||
|
|
||||||
|
abortExistingInflation(key);
|
||||||
|
|
||||||
boolean lifetimeExtended = false;
|
boolean lifetimeExtended = false;
|
||||||
|
|
||||||
// Notification was canceled before it got inflated
|
|
||||||
if (entry == null) {
|
|
||||||
NotificationEntry pendingEntry = mPendingNotifications.get(key);
|
|
||||||
if (pendingEntry != null) {
|
|
||||||
for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
|
|
||||||
if (extender.shouldExtendLifetimeForPendingNotification(pendingEntry)) {
|
|
||||||
extendLifetime(pendingEntry, extender);
|
|
||||||
lifetimeExtended = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lifetimeExtended) {
|
|
||||||
abortExistingInflation(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
// If a manager needs to keep the notification around for whatever reason, we
|
// If a manager needs to keep the notification around for whatever reason, we
|
||||||
// keep the notification
|
// keep the notification
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
|
|||||||
mVisualStabilityManager.setUpWithPresenter(this);
|
mVisualStabilityManager.setUpWithPresenter(this);
|
||||||
mGutsManager.setUpWithPresenter(this,
|
mGutsManager.setUpWithPresenter(this,
|
||||||
notifListContainer, mCheckSaveListener, mOnSettingsClickListener);
|
notifListContainer, mCheckSaveListener, mOnSettingsClickListener);
|
||||||
// ForegroundServiceNotificationListener adds its listener in its constructor
|
// ForegroundServiceControllerListener adds its listener in its constructor
|
||||||
// but we need to request it here in order for it to be instantiated.
|
// but we need to request it here in order for it to be instantiated.
|
||||||
// TODO: figure out how to do this correctly once Dependency.get() is gone.
|
// TODO: figure out how to do this correctly once Dependency.get() is gone.
|
||||||
Dependency.get(ForegroundServiceNotificationListener.class);
|
Dependency.get(ForegroundServiceNotificationListener.class);
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2019 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.systemui;
|
|
||||||
|
|
||||||
import static com.android.systemui.ForegroundServiceNotificationListener.ForegroundServiceLifetimeExtender.MIN_FGS_TIME_MS;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.app.Notification;
|
|
||||||
import android.service.notification.StatusBarNotification;
|
|
||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
|
||||||
|
|
||||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
@SmallTest
|
|
||||||
public class ForegroundServiceNotificationListenerTest extends SysuiTestCase {
|
|
||||||
private ForegroundServiceNotificationListener.ForegroundServiceLifetimeExtender mExtender =
|
|
||||||
new ForegroundServiceNotificationListener.ForegroundServiceLifetimeExtender();
|
|
||||||
private StatusBarNotification mSbn;
|
|
||||||
private NotificationEntry mEntry;
|
|
||||||
private Notification mNotif;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setup() {
|
|
||||||
mNotif = new Notification.Builder(mContext, "")
|
|
||||||
.setSmallIcon(R.drawable.ic_person)
|
|
||||||
.setContentTitle("Title")
|
|
||||||
.setContentText("Text")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
mSbn = mock(StatusBarNotification.class);
|
|
||||||
when(mSbn.getNotification()).thenReturn(mNotif);
|
|
||||||
|
|
||||||
mEntry = new NotificationEntry(mSbn);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ForegroundServiceLifetimeExtenderTest
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testShouldExtendLifetime_should_foreground() {
|
|
||||||
// Extend the lifetime of a FGS notification iff it has not been visible
|
|
||||||
// for the minimum time
|
|
||||||
mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
|
|
||||||
when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis());
|
|
||||||
assertTrue(mExtender.shouldExtendLifetime(mEntry));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testShouldExtendLifetime_shouldNot_foreground() {
|
|
||||||
mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
|
|
||||||
when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1);
|
|
||||||
assertFalse(mExtender.shouldExtendLifetime(mEntry));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testShouldExtendLifetime_shouldNot_notForeground() {
|
|
||||||
mNotif.flags = 0;
|
|
||||||
when(mSbn.getPostTime()).thenReturn(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1);
|
|
||||||
assertFalse(mExtender.shouldExtendLifetime(mEntry));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5357,16 +5357,8 @@ public class NotificationManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
synchronized (mNotificationLock) {
|
synchronized (mNotificationLock) {
|
||||||
// If the notification is currently enqueued, repost this runnable so it has a
|
// Look for the notification, searching both the posted and enqueued lists.
|
||||||
// chance to notify listeners
|
NotificationRecord r = findNotificationLocked(mPkg, mTag, mId, mUserId);
|
||||||
if ((findNotificationByListLocked(mEnqueuedNotifications, mPkg, mTag, mId, mUserId))
|
|
||||||
!= null) {
|
|
||||||
mHandler.post(this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Look for the notification in the posted list, since we already checked enqueued.
|
|
||||||
NotificationRecord r =
|
|
||||||
findNotificationByListLocked(mNotificationList, mPkg, mTag, mId, mUserId);
|
|
||||||
if (r != null) {
|
if (r != null) {
|
||||||
// The notification was found, check if it should be removed.
|
// The notification was found, check if it should be removed.
|
||||||
|
|
||||||
|
|||||||
@@ -1016,20 +1016,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
|||||||
assertEquals(0, mService.getNotificationRecordCount());
|
assertEquals(0, mService.getNotificationRecordCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCancelImmediatelyAfterEnqueueNotifiesListeners_ForegroundServiceFlag()
|
|
||||||
throws Exception {
|
|
||||||
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
|
|
||||||
sbn.getNotification().flags =
|
|
||||||
Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE;
|
|
||||||
mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
|
|
||||||
sbn.getId(), sbn.getNotification(), sbn.getUserId());
|
|
||||||
mBinderService.cancelNotificationWithTag(PKG, "tag", sbn.getId(), sbn.getUserId());
|
|
||||||
waitForIdle();
|
|
||||||
verify(mListeners, times(1)).notifyPostedLocked(any(), any());
|
|
||||||
verify(mListeners, times(1)).notifyRemovedLocked(any(), anyInt(), any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUserInitiatedClearAll_noLeak() throws Exception {
|
public void testUserInitiatedClearAll_noLeak() throws Exception {
|
||||||
final NotificationRecord n = generateNotificationRecord(
|
final NotificationRecord n = generateNotificationRecord(
|
||||||
|
|||||||
Reference in New Issue
Block a user