blob: ef1f4e0afeded26f9c801fa2942ce9d0b72f8314 [file] [log] [blame]
/*
* Copyright (C) 2018 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 android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.Log;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.util.time.SystemClock;
import javax.inject.Inject;
import javax.inject.Singleton;
/** Updates foreground service notification state in response to notification data events. */
@Singleton
public class ForegroundServiceNotificationListener {
private static final String TAG = "FgServiceController";
private static final boolean DBG = false;
private final Context mContext;
private final ForegroundServiceController mForegroundServiceController;
private final NotificationEntryManager mEntryManager;
@Inject
public ForegroundServiceNotificationListener(Context context,
ForegroundServiceController foregroundServiceController,
NotificationEntryManager notificationEntryManager,
NotifPipeline notifPipeline,
SystemClock systemClock) {
mContext = context;
mForegroundServiceController = foregroundServiceController;
// TODO: (b/145659174) remove mEntryManager when moving to NewNotifPipeline. Replaced by
// ForegroundCoordinator
mEntryManager = notificationEntryManager;
mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
addNotification(entry, entry.getImportance());
}
@Override
public void onPreEntryUpdated(NotificationEntry entry) {
updateNotification(entry, entry.getImportance());
}
@Override
public void onEntryRemoved(
NotificationEntry entry,
NotificationVisibility visibility,
boolean removedByUser,
int reason) {
removeNotification(entry.getSbn());
}
});
mEntryManager.addNotificationLifetimeExtender(
new ForegroundServiceLifetimeExtender(systemClock));
notifPipeline.addCollectionListener(new NotifCollectionListener() {
@Override
public void onEntryAdded(NotificationEntry entry) {
addNotification(entry, entry.getImportance());
}
@Override
public void onEntryUpdated(NotificationEntry entry) {
updateNotification(entry, entry.getImportance());
}
@Override
public void onEntryRemoved(NotificationEntry entry, int reason) {
removeNotification(entry.getSbn());
}
});
}
/**
* @param entry notification that was just posted
*/
private void addNotification(NotificationEntry entry, int importance) {
updateNotification(entry, importance);
}
/**
* @param sbn notification that was just removed
*/
private void removeNotification(StatusBarNotification sbn) {
mForegroundServiceController.updateUserState(
sbn.getUserId(),
new ForegroundServiceController.UserStateUpdateCallback() {
@Override
public boolean updateUserState(ForegroundServicesUserState userState) {
if (mForegroundServiceController.isDisclosureNotification(sbn)) {
// if you remove the dungeon entirely, we take that to mean there are
// no running services
userState.setRunningServices(null, 0);
return true;
} else {
// this is safe to call on any notification, not just
// FLAG_FOREGROUND_SERVICE
return userState.removeNotification(sbn.getPackageName(), sbn.getKey());
}
}
@Override
public void userStateNotFound(int userId) {
if (DBG) {
Log.w(TAG, String.format(
"user %d with no known notifications got removeNotification "
+ "for %s",
sbn.getUserId(), sbn));
}
}
},
false /* don't create */);
}
/**
* @param entry notification that was just changed in some way
*/
private void updateNotification(NotificationEntry entry, int newImportance) {
final StatusBarNotification sbn = entry.getSbn();
mForegroundServiceController.updateUserState(
sbn.getUserId(),
userState -> {
if (mForegroundServiceController.isDisclosureNotification(sbn)) {
final Bundle extras = sbn.getNotification().extras;
if (extras != null) {
final String[] svcs = extras.getStringArray(
Notification.EXTRA_FOREGROUND_APPS);
userState.setRunningServices(svcs, sbn.getNotification().when);
}
} else {
userState.removeNotification(sbn.getPackageName(), sbn.getKey());
if (0 != (sbn.getNotification().flags
& Notification.FLAG_FOREGROUND_SERVICE)) {
if (newImportance > NotificationManager.IMPORTANCE_MIN) {
userState.addImportantNotification(sbn.getPackageName(),
sbn.getKey());
}
final Notification.Builder builder =
Notification.Builder.recoverBuilder(
mContext, sbn.getNotification());
if (builder.usesStandardHeader()) {
userState.addStandardLayoutNotification(
sbn.getPackageName(), sbn.getKey());
}
}
}
tagForeground(entry);
return true;
},
true /* create if not found */);
}
// TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
// ForegroundCoordinator
private void tagForeground(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
sbn.getUserId(),
sbn.getPackageName());
if (activeOps != null) {
synchronized (entry.mActiveAppOps) {
entry.mActiveAppOps.clear();
entry.mActiveAppOps.addAll(activeOps);
}
}
}
}