resolve merge conflicts of 88c651eab1 to oc-dr1-dev
Bug: 119041698 Test: atest SystemUITests Change-Id: I9d1547f98cc19111ce2dc8ab2f4e2bf2d3cb0baa Merged-In: I0680034ed9315aa2c05282524d48faaed066ebd0
This commit is contained in:
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* 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.statusbar;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.ArraySet;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.systemui.statusbar.NotificationData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends the lifetime of foreground notification services such that they show for at least
|
||||||
|
* five seconds
|
||||||
|
*/
|
||||||
|
public class ForegroundServiceLifetimeExtender implements NotificationLifetimeExtender {
|
||||||
|
private static final String TAG = "FGSLifetimeExtender";
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public static final int MIN_FGS_TIME_MS = 5000;
|
||||||
|
|
||||||
|
private NotificationSafeToRemoveCallback mNotificationSafeToRemoveCallback;
|
||||||
|
private ArraySet<NotificationData.Entry> 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 NotificationData.Entry 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 NotificationData.Entry entry) {
|
||||||
|
return shouldExtendLifetime(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setShouldManageLifetime(
|
||||||
|
@NonNull NotificationData.Entry entry, boolean shouldManage) {
|
||||||
|
android.util.Log.d("FGSExtender", "setShouldManageLifetime " + shouldManage);
|
||||||
|
if (!shouldManage) {
|
||||||
|
mManagedEntries.remove(entry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mManagedEntries.add(entry);
|
||||||
|
Runnable r = () -> {
|
||||||
|
if (mManagedEntries.contains(entry)) {
|
||||||
|
mManagedEntries.remove(entry);
|
||||||
|
if (mNotificationSafeToRemoveCallback != null) {
|
||||||
|
mNotificationSafeToRemoveCallback.onSafeToRemove(entry.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
long delayAmt = MIN_FGS_TIME_MS
|
||||||
|
- (System.currentTimeMillis() - entry.notification.getPostTime());
|
||||||
|
mHandler.postDelayed(r, delayAmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* 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.statusbar;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.android.systemui.statusbar.NotificationData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for anything that may need to keep notifications managed even after
|
||||||
|
* {@link NotificationListener} removes it. The lifetime extender is in charge of performing the
|
||||||
|
* callback when the notification is then safe to remove.
|
||||||
|
*/
|
||||||
|
public interface NotificationLifetimeExtender {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the handler to callback to when the notification is safe to remove.
|
||||||
|
*
|
||||||
|
* @param callback the handler to callback
|
||||||
|
*/
|
||||||
|
void setCallback(@NonNull NotificationSafeToRemoveCallback callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether or not the extender needs the notification kept after removal.
|
||||||
|
*
|
||||||
|
* @param entry the entry containing the notification to check
|
||||||
|
* @return true if the notification lifetime should be extended
|
||||||
|
*/
|
||||||
|
boolean shouldExtendLifetime(@NonNull NotificationData.Entry 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 NotificationData.Entry pendingEntry) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* now responsible for calling {@link NotificationSafeToRemoveCallback#onSafeToRemove(String)}
|
||||||
|
* when the entry is safe to remove. If shouldManage is false, the extender no longer needs to
|
||||||
|
* worry about it (either because we will be removing it anyway or the entry is no longer
|
||||||
|
* removed due to an update).
|
||||||
|
*
|
||||||
|
* @param entry the entry that needs an extended lifetime
|
||||||
|
* @param shouldManage true if the extender should manage the entry now, false otherwise
|
||||||
|
*/
|
||||||
|
void setShouldManageLifetime(@NonNull NotificationData.Entry entry, boolean shouldManage);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The callback for when the notification is now safe to remove (i.e. its lifetime has ended).
|
||||||
|
*/
|
||||||
|
interface NotificationSafeToRemoveCallback {
|
||||||
|
/**
|
||||||
|
* Called when the lifetime extender determines it's safe to remove.
|
||||||
|
*
|
||||||
|
* @param key key of the entry that is now safe to remove
|
||||||
|
*/
|
||||||
|
void onSafeToRemove(String key);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -112,6 +112,7 @@ import android.service.vr.IVrManager;
|
|||||||
import android.service.vr.IVrStateCallbacks;
|
import android.service.vr.IVrStateCallbacks;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
|
import android.util.ArrayMap;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.EventLog;
|
import android.util.EventLog;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -206,6 +207,7 @@ import com.android.systemui.statusbar.DismissView;
|
|||||||
import com.android.systemui.statusbar.DragDownHelper;
|
import com.android.systemui.statusbar.DragDownHelper;
|
||||||
import com.android.systemui.statusbar.EmptyShadeView;
|
import com.android.systemui.statusbar.EmptyShadeView;
|
||||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||||
|
import com.android.systemui.statusbar.ForegroundServiceLifetimeExtender;
|
||||||
import com.android.systemui.statusbar.GestureRecorder;
|
import com.android.systemui.statusbar.GestureRecorder;
|
||||||
import com.android.systemui.statusbar.KeyboardShortcuts;
|
import com.android.systemui.statusbar.KeyboardShortcuts;
|
||||||
import com.android.systemui.statusbar.KeyguardIndicationController;
|
import com.android.systemui.statusbar.KeyguardIndicationController;
|
||||||
@@ -213,6 +215,7 @@ import com.android.systemui.statusbar.NotificationData;
|
|||||||
import com.android.systemui.statusbar.NotificationData.Entry;
|
import com.android.systemui.statusbar.NotificationData.Entry;
|
||||||
import com.android.systemui.statusbar.NotificationGuts;
|
import com.android.systemui.statusbar.NotificationGuts;
|
||||||
import com.android.systemui.statusbar.NotificationInfo;
|
import com.android.systemui.statusbar.NotificationInfo;
|
||||||
|
import com.android.systemui.statusbar.NotificationLifetimeExtender;
|
||||||
import com.android.systemui.statusbar.NotificationShelf;
|
import com.android.systemui.statusbar.NotificationShelf;
|
||||||
import com.android.systemui.statusbar.NotificationSnooze;
|
import com.android.systemui.statusbar.NotificationSnooze;
|
||||||
import com.android.systemui.statusbar.RemoteInputController;
|
import com.android.systemui.statusbar.RemoteInputController;
|
||||||
@@ -745,10 +748,11 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
@Nullable private View mAmbientIndicationContainer;
|
@Nullable private View mAmbientIndicationContainer;
|
||||||
private String mKeyToRemoveOnGutsClosed;
|
private String mKeyToRemoveOnGutsClosed;
|
||||||
private SysuiColorExtractor mColorExtractor;
|
private SysuiColorExtractor mColorExtractor;
|
||||||
private ForegroundServiceController mForegroundServiceController;
|
|
||||||
private ScreenLifecycle mScreenLifecycle;
|
private ScreenLifecycle mScreenLifecycle;
|
||||||
@VisibleForTesting WakefulnessLifecycle mWakefulnessLifecycle;
|
@VisibleForTesting WakefulnessLifecycle mWakefulnessLifecycle;
|
||||||
|
|
||||||
|
protected ForegroundServiceController mForegroundServiceController;
|
||||||
|
|
||||||
private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) {
|
private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) {
|
||||||
final int N = array.size();
|
final int N = array.size();
|
||||||
for (int i = 0 ; i < N; i++) {
|
for (int i = 0 ; i < N; i++) {
|
||||||
@@ -803,6 +807,9 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
|
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
|
||||||
|
|
||||||
mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
|
mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
|
||||||
|
mFGSExtender = new ForegroundServiceLifetimeExtender();
|
||||||
|
mFGSExtender.setCallback(key -> removeNotification(key, mLatestRankingMap));
|
||||||
|
|
||||||
|
|
||||||
mDisplay = mWindowManager.getDefaultDisplay();
|
mDisplay = mWindowManager.getDefaultDisplay();
|
||||||
updateDisplaySize();
|
updateDisplaySize();
|
||||||
@@ -1803,6 +1810,11 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
}
|
}
|
||||||
Entry entry = mNotificationData.get(key);
|
Entry entry = mNotificationData.get(key);
|
||||||
|
|
||||||
|
if (entry != null && mFGSExtender.shouldExtendLifetime(entry)) {
|
||||||
|
extendLifetime(entry, mFGSExtender);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (entry != null && mRemoteInputController.isRemoteInputActive(entry)
|
if (entry != null && mRemoteInputController.isRemoteInputActive(entry)
|
||||||
&& (entry.row != null && !entry.row.isDismissed())) {
|
&& (entry.row != null && !entry.row.isDismissed())) {
|
||||||
mLatestRankingMap = ranking;
|
mLatestRankingMap = ranking;
|
||||||
@@ -1841,9 +1853,35 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Make sure no lifetime extension is happening anymore
|
||||||
|
cancelLifetimeExtension(entry);
|
||||||
setAreThereNotifications();
|
setAreThereNotifications();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Lifetime extension keeps entries around after they would've otherwise been canceled */
|
||||||
|
private void extendLifetime(Entry entry, NotificationLifetimeExtender extender) {
|
||||||
|
// Cancel any other extender which might be holding on to this notification entry
|
||||||
|
NotificationLifetimeExtender activeExtender = mRetainedNotifications.get(entry);
|
||||||
|
if (activeExtender != null && activeExtender != extender) {
|
||||||
|
activeExtender.setShouldManageLifetime(entry, false);
|
||||||
|
}
|
||||||
|
mRetainedNotifications.put(entry, extender);
|
||||||
|
extender.setShouldManageLifetime(entry, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tells the current extender (if any) to stop extending the entry's lifetime */
|
||||||
|
private void cancelLifetimeExtension(NotificationData.Entry entry) {
|
||||||
|
NotificationLifetimeExtender activeExtender = mRetainedNotifications.remove(entry);
|
||||||
|
if (activeExtender != null) {
|
||||||
|
activeExtender.setShouldManageLifetime(entry, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public Map<Entry, NotificationLifetimeExtender> getRetainedNotificationMap() {
|
||||||
|
return mRetainedNotifications;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures that the group children are cancelled immediately when the group summary is cancelled
|
* Ensures that the group children are cancelled immediately when the group summary is cancelled
|
||||||
* instead of waiting for the notification manager to send all cancels. Otherwise this could
|
* instead of waiting for the notification manager to send all cancels. Otherwise this could
|
||||||
@@ -3479,6 +3517,17 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pw.println(" Lifetime-extended notifications:");
|
||||||
|
if (mRetainedNotifications.isEmpty()) {
|
||||||
|
pw.println(" None");
|
||||||
|
} else {
|
||||||
|
for (Map.Entry<NotificationData.Entry, NotificationLifetimeExtender> entry
|
||||||
|
: mRetainedNotifications.entrySet()) {
|
||||||
|
pw.println(" " + entry.getKey().notification + " retained by "
|
||||||
|
+ entry.getValue().getClass().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pw.print(" mInteractingWindows="); pw.println(mInteractingWindows);
|
pw.print(" mInteractingWindows="); pw.println(mInteractingWindows);
|
||||||
pw.print(" mStatusBarWindowState=");
|
pw.print(" mStatusBarWindowState=");
|
||||||
pw.println(windowStateToString(mStatusBarWindowState));
|
pw.println(windowStateToString(mStatusBarWindowState));
|
||||||
@@ -5622,6 +5671,11 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
|
|
||||||
protected RemoteInputController mRemoteInputController;
|
protected RemoteInputController mRemoteInputController;
|
||||||
|
|
||||||
|
// A lifetime extender that watches for foreground service notifications
|
||||||
|
@VisibleForTesting protected NotificationLifetimeExtender mFGSExtender;
|
||||||
|
private final Map<Entry, NotificationLifetimeExtender> mRetainedNotifications =
|
||||||
|
new ArrayMap<>();
|
||||||
|
|
||||||
// for heads up notifications
|
// for heads up notifications
|
||||||
protected HeadsUpManager mHeadsUpManager;
|
protected HeadsUpManager mHeadsUpManager;
|
||||||
|
|
||||||
@@ -7288,6 +7342,9 @@ public class StatusBar extends SystemUI implements DemoMode,
|
|||||||
Log.w(TAG, "Notification that was kept for guts was updated. " + key);
|
Log.w(TAG, "Notification that was kept for guts was updated. " + key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No need to keep the lifetime extension around if an update comes in for it
|
||||||
|
cancelLifetimeExtension(entry);
|
||||||
|
|
||||||
Notification n = notification.getNotification();
|
Notification n = notification.getNotification();
|
||||||
mNotificationData.updateRanking(ranking);
|
mNotificationData.updateRanking(ranking);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* 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.statusbar;
|
||||||
|
|
||||||
|
import static com.android.systemui.statusbar.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.NotificationListenerService.Ranking;
|
||||||
|
import android.service.notification.StatusBarNotification;
|
||||||
|
import android.support.test.filters.SmallTest;
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import com.android.systemui.R;
|
||||||
|
import com.android.systemui.SysuiTestCase;
|
||||||
|
import com.android.systemui.statusbar.NotificationData.Entry;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class ForegroundServiceLifetimeExtenderTest extends SysuiTestCase {
|
||||||
|
private ForegroundServiceLifetimeExtender mExtender = new ForegroundServiceLifetimeExtender();
|
||||||
|
private StatusBarNotification mSbn;
|
||||||
|
private NotificationData.Entry 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 NotificationData.Entry(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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.phone;
|
|||||||
|
|
||||||
import static android.app.NotificationManager.IMPORTANCE_HIGH;
|
import static android.app.NotificationManager.IMPORTANCE_HIGH;
|
||||||
|
|
||||||
|
import static com.android.systemui.statusbar.ForegroundServiceLifetimeExtender.MIN_FGS_TIME_MS;
|
||||||
|
|
||||||
import static junit.framework.Assert.assertFalse;
|
import static junit.framework.Assert.assertFalse;
|
||||||
import static junit.framework.Assert.assertTrue;
|
import static junit.framework.Assert.assertTrue;
|
||||||
import static junit.framework.TestCase.fail;
|
import static junit.framework.TestCase.fail;
|
||||||
@@ -37,6 +39,7 @@ import static org.mockito.Mockito.times;
|
|||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.app.ActivityManager;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.trust.TrustManager;
|
import android.app.trust.TrustManager;
|
||||||
import android.hardware.fingerprint.FingerprintManager;
|
import android.hardware.fingerprint.FingerprintManager;
|
||||||
@@ -49,6 +52,7 @@ import android.os.PowerManager;
|
|||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
|
import android.service.notification.NotificationListenerService.RankingMap;
|
||||||
import android.support.test.filters.SmallTest;
|
import android.support.test.filters.SmallTest;
|
||||||
import android.support.test.metricshelper.MetricsAsserts;
|
import android.support.test.metricshelper.MetricsAsserts;
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
@@ -64,6 +68,11 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
|||||||
import com.android.internal.logging.testing.FakeMetricsLogger;
|
import com.android.internal.logging.testing.FakeMetricsLogger;
|
||||||
import com.android.internal.statusbar.IStatusBarService;
|
import com.android.internal.statusbar.IStatusBarService;
|
||||||
import com.android.keyguard.KeyguardHostView.OnDismissAction;
|
import com.android.keyguard.KeyguardHostView.OnDismissAction;
|
||||||
|
import com.android.systemui.ForegroundServiceController;
|
||||||
|
import com.android.systemui.R;
|
||||||
|
import com.android.systemui.statusbar.ForegroundServiceLifetimeExtender;
|
||||||
|
import com.android.systemui.statusbar.NotificationLifetimeExtender;
|
||||||
|
import com.android.systemui.statusbar.RemoteInputController;
|
||||||
import com.android.systemui.SysuiTestCase;
|
import com.android.systemui.SysuiTestCase;
|
||||||
import com.android.systemui.keyguard.WakefulnessLifecycle;
|
import com.android.systemui.keyguard.WakefulnessLifecycle;
|
||||||
import com.android.systemui.recents.misc.SystemServicesProxy;
|
import com.android.systemui.recents.misc.SystemServicesProxy;
|
||||||
@@ -83,12 +92,18 @@ import org.junit.runner.RunWith;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import junit.framework.Assert;
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidTestingRunner.class)
|
@RunWith(AndroidTestingRunner.class)
|
||||||
@RunWithLooper
|
@RunWithLooper
|
||||||
public class StatusBarTest extends SysuiTestCase {
|
public class StatusBarTest extends SysuiTestCase {
|
||||||
|
|
||||||
|
private static final String TEST_PACKAGE_NAME = "test";
|
||||||
|
private static final int TEST_UID = 123;
|
||||||
|
|
||||||
StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
|
StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
|
||||||
UnlockMethodCache mUnlockMethodCache;
|
UnlockMethodCache mUnlockMethodCache;
|
||||||
KeyguardIndicationController mKeyguardIndicationController;
|
KeyguardIndicationController mKeyguardIndicationController;
|
||||||
@@ -101,8 +116,12 @@ public class StatusBarTest extends SysuiTestCase {
|
|||||||
SystemServicesProxy mSystemServicesProxy;
|
SystemServicesProxy mSystemServicesProxy;
|
||||||
NotificationPanelView mNotificationPanelView;
|
NotificationPanelView mNotificationPanelView;
|
||||||
IStatusBarService mBarService;
|
IStatusBarService mBarService;
|
||||||
|
RemoteInputController mRemoteInputController;
|
||||||
|
ForegroundServiceController mForegroundServiceController;
|
||||||
ArrayList<Entry> mNotificationList;
|
ArrayList<Entry> mNotificationList;
|
||||||
private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
|
private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
|
||||||
|
private ForegroundServiceLifetimeExtender mFGSExtender =
|
||||||
|
new ForegroundServiceLifetimeExtender();
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
@@ -120,6 +139,8 @@ public class StatusBarTest extends SysuiTestCase {
|
|||||||
when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0));
|
when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0));
|
||||||
mNotificationList = mock(ArrayList.class);
|
mNotificationList = mock(ArrayList.class);
|
||||||
IPowerManager powerManagerService = mock(IPowerManager.class);
|
IPowerManager powerManagerService = mock(IPowerManager.class);
|
||||||
|
mRemoteInputController = mock(RemoteInputController.class);
|
||||||
|
mForegroundServiceController = mock(ForegroundServiceController.class);
|
||||||
HandlerThread handlerThread = new HandlerThread("TestThread");
|
HandlerThread handlerThread = new HandlerThread("TestThread");
|
||||||
handlerThread.start();
|
handlerThread.start();
|
||||||
mPowerManager = new PowerManager(mContext, powerManagerService,
|
mPowerManager = new PowerManager(mContext, powerManagerService,
|
||||||
@@ -128,11 +149,12 @@ public class StatusBarTest extends SysuiTestCase {
|
|||||||
mBarService = mock(IStatusBarService.class);
|
mBarService = mock(IStatusBarService.class);
|
||||||
|
|
||||||
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
|
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
|
||||||
|
mFGSExtender.setCallback(key -> mStatusBar.removeNotification(key, mock(RankingMap.class)));
|
||||||
mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
|
mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
|
||||||
mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
|
mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
|
||||||
mNotificationData, mPowerManager, mSystemServicesProxy, mNotificationPanelView,
|
mNotificationData, mPowerManager, mSystemServicesProxy, mNotificationPanelView,
|
||||||
mBarService);
|
mBarService, mFGSExtender, mRemoteInputController, mForegroundServiceController);
|
||||||
mStatusBar.mContext = mContext;
|
|
||||||
doAnswer(invocation -> {
|
doAnswer(invocation -> {
|
||||||
OnDismissAction onDismissAction = (OnDismissAction) invocation.getArguments()[0];
|
OnDismissAction onDismissAction = (OnDismissAction) invocation.getArguments()[0];
|
||||||
onDismissAction.onDismiss();
|
onDismissAction.onDismiss();
|
||||||
@@ -410,6 +432,53 @@ public class StatusBarTest extends SysuiTestCase {
|
|||||||
assertTrue(mStatusBar.shouldPeek(entry, sbn));
|
assertTrue(mStatusBar.shouldPeek(entry, sbn));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testForegroundServiceNotificationKeptForFiveSeconds() throws Exception {
|
||||||
|
RankingMap rm = mock(RankingMap.class);
|
||||||
|
|
||||||
|
// sbn posted "just now"
|
||||||
|
Notification n = new Notification.Builder(mContext, "")
|
||||||
|
.setSmallIcon(R.drawable.ic_person)
|
||||||
|
.setContentTitle("Title")
|
||||||
|
.setContentText("Text")
|
||||||
|
.build();
|
||||||
|
n.flags |= Notification.FLAG_FOREGROUND_SERVICE;
|
||||||
|
StatusBarNotification sbn =
|
||||||
|
new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
|
||||||
|
0, n, new UserHandle(ActivityManager.getCurrentUser()), null,
|
||||||
|
System.currentTimeMillis());
|
||||||
|
NotificationData.Entry entry = new NotificationData.Entry(sbn);
|
||||||
|
when(mNotificationData.get(any())).thenReturn(entry);
|
||||||
|
mStatusBar.removeNotification(sbn.getKey(), rm);
|
||||||
|
Map<NotificationData.Entry, NotificationLifetimeExtender> map =
|
||||||
|
mStatusBar.getRetainedNotificationMap();
|
||||||
|
Assert.assertTrue(map.containsKey(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testForegroundServiceNotification_notRetainedIfShownForFiveSeconds()
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
RankingMap rm = mock(RankingMap.class);
|
||||||
|
|
||||||
|
// sbn posted "more than 5 seconds ago"
|
||||||
|
Notification n = new Notification.Builder(mContext, "")
|
||||||
|
.setSmallIcon(R.drawable.ic_person)
|
||||||
|
.setContentTitle("Title")
|
||||||
|
.setContentText("Text")
|
||||||
|
.build();
|
||||||
|
n.flags |= Notification.FLAG_FOREGROUND_SERVICE;
|
||||||
|
StatusBarNotification sbn =
|
||||||
|
new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
|
||||||
|
0, n, new UserHandle(ActivityManager.getCurrentUser()), null,
|
||||||
|
System.currentTimeMillis() - MIN_FGS_TIME_MS - 1);
|
||||||
|
NotificationData.Entry entry = new NotificationData.Entry(sbn);
|
||||||
|
when(mNotificationData.get(any())).thenReturn(entry);
|
||||||
|
mStatusBar.removeNotification(sbn.getKey(), rm);
|
||||||
|
Map<NotificationData.Entry, NotificationLifetimeExtender> map =
|
||||||
|
mStatusBar.getRetainedNotificationMap();
|
||||||
|
Assert.assertFalse(map.containsKey(entry));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLogHidden() {
|
public void testLogHidden() {
|
||||||
@@ -489,7 +558,8 @@ public class StatusBarTest extends SysuiTestCase {
|
|||||||
UnlockMethodCache unlock, KeyguardIndicationController key,
|
UnlockMethodCache unlock, KeyguardIndicationController key,
|
||||||
NotificationStackScrollLayout stack, HeadsUpManager hum, NotificationData nd,
|
NotificationStackScrollLayout stack, HeadsUpManager hum, NotificationData nd,
|
||||||
PowerManager pm, SystemServicesProxy ssp, NotificationPanelView panelView,
|
PowerManager pm, SystemServicesProxy ssp, NotificationPanelView panelView,
|
||||||
IStatusBarService barService) {
|
IStatusBarService barService, ForegroundServiceLifetimeExtender fgsExtender,
|
||||||
|
RemoteInputController ric, ForegroundServiceController fsc) {
|
||||||
mStatusBarKeyguardViewManager = man;
|
mStatusBarKeyguardViewManager = man;
|
||||||
mUnlockMethodCache = unlock;
|
mUnlockMethodCache = unlock;
|
||||||
mKeyguardIndicationController = key;
|
mKeyguardIndicationController = key;
|
||||||
@@ -501,6 +571,9 @@ public class StatusBarTest extends SysuiTestCase {
|
|||||||
mSystemServicesProxy = ssp;
|
mSystemServicesProxy = ssp;
|
||||||
mNotificationPanel = panelView;
|
mNotificationPanel = panelView;
|
||||||
mBarService = barService;
|
mBarService = barService;
|
||||||
|
mFGSExtender = fgsExtender;
|
||||||
|
mRemoteInputController = ric;
|
||||||
|
mForegroundServiceController = fsc;
|
||||||
mWakefulnessLifecycle = createAwakeWakefulnessLifecycle();
|
mWakefulnessLifecycle = createAwakeWakefulnessLifecycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,6 +584,7 @@ public class StatusBarTest extends SysuiTestCase {
|
|||||||
return wakefulnessLifecycle;
|
return wakefulnessLifecycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setBarStateForTest(int state) {
|
public void setBarStateForTest(int state) {
|
||||||
mState = state;
|
mState = state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4442,8 +4442,15 @@ public class NotificationManagerService extends SystemService {
|
|||||||
userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
|
userId, mustHaveFlags, mustNotHaveFlags, reason, listenerName);
|
||||||
|
|
||||||
synchronized (mNotificationLock) {
|
synchronized (mNotificationLock) {
|
||||||
// Look for the notification, searching both the posted and enqueued lists.
|
// If the notification is currently enqueued, repost this runnable so it has a
|
||||||
NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
|
// chance to notify listeners
|
||||||
|
if ((findNotificationByListLocked(
|
||||||
|
mEnqueuedNotifications, pkg, tag, id, userId)) != null) {
|
||||||
|
mHandler.post(this);
|
||||||
|
}
|
||||||
|
// Look for the notification in the posted list, since we already checked enq
|
||||||
|
NotificationRecord r = findNotificationByListLocked(
|
||||||
|
mNotificationList, pkg, tag, id, userId);
|
||||||
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.
|
||||||
|
|
||||||
|
|||||||
@@ -336,6 +336,21 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
|
|||||||
assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
|
assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCancelImmediatelyAfterEnqueueNotifiesListeners_ForegroundServiceFlag()
|
||||||
|
throws Exception {
|
||||||
|
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
|
||||||
|
sbn.getNotification().flags =
|
||||||
|
Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE;
|
||||||
|
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
|
||||||
|
sbn.getId(), sbn.getNotification(), sbn.getUserId());
|
||||||
|
mBinderService.cancelNotificationWithTag(PKG, "tag", sbn.getId(), sbn.getUserId());
|
||||||
|
waitForIdle();
|
||||||
|
verify(mNotificationListeners, times(1)).notifyPostedLocked(any(), any());
|
||||||
|
verify(mNotificationListeners, times(1)).notifyRemovedLocked(any(), anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCancelNotificationWhilePostedAndEnqueued() throws Exception {
|
public void testCancelNotificationWhilePostedAndEnqueued() throws Exception {
|
||||||
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
|
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user