1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui;
18 
19 import android.app.Notification;
20 import android.app.NotificationManager;
21 import android.content.Context;
22 import android.os.Bundle;
23 import android.service.notification.StatusBarNotification;
24 import android.util.Log;
25 
26 import com.android.internal.statusbar.NotificationVisibility;
27 import com.android.systemui.statusbar.notification.NotificationEntryListener;
28 import com.android.systemui.statusbar.notification.NotificationEntryManager;
29 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
30 
31 import javax.inject.Inject;
32 import javax.inject.Singleton;
33 
34 /** Updates foreground service notification state in response to notification data events. */
35 @Singleton
36 public class ForegroundServiceNotificationListener {
37 
38     private static final String TAG = "FgServiceController";
39     private static final boolean DBG = false;
40 
41     private final Context mContext;
42     private final ForegroundServiceController mForegroundServiceController;
43 
44     @Inject
ForegroundServiceNotificationListener(Context context, ForegroundServiceController foregroundServiceController, NotificationEntryManager notificationEntryManager)45     public ForegroundServiceNotificationListener(Context context,
46             ForegroundServiceController foregroundServiceController,
47             NotificationEntryManager notificationEntryManager) {
48         mContext = context;
49         mForegroundServiceController = foregroundServiceController;
50         notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
51             @Override
52             public void onPendingEntryAdded(NotificationEntry entry) {
53                 addNotification(entry.notification, entry.importance);
54             }
55 
56             @Override
57             public void onPostEntryUpdated(NotificationEntry entry) {
58                 updateNotification(entry.notification, entry.importance);
59             }
60 
61             @Override
62             public void onEntryRemoved(
63                     NotificationEntry entry,
64                     NotificationVisibility visibility,
65                     boolean removedByUser) {
66                 removeNotification(entry.notification);
67             }
68         });
69     }
70 
71     /**
72      * @param sbn notification that was just posted
73      */
addNotification(StatusBarNotification sbn, int importance)74     private void addNotification(StatusBarNotification sbn, int importance) {
75         updateNotification(sbn, importance);
76     }
77 
78     /**
79      * @param sbn notification that was just removed
80      */
removeNotification(StatusBarNotification sbn)81     private void removeNotification(StatusBarNotification sbn) {
82         mForegroundServiceController.updateUserState(
83                 sbn.getUserId(),
84                 new ForegroundServiceController.UserStateUpdateCallback() {
85                     @Override
86                     public boolean updateUserState(ForegroundServicesUserState userState) {
87                         if (mForegroundServiceController.isDisclosureNotification(sbn)) {
88                             // if you remove the dungeon entirely, we take that to mean there are
89                             // no running services
90                             userState.setRunningServices(null, 0);
91                             return true;
92                         } else {
93                             // this is safe to call on any notification, not just
94                             // FLAG_FOREGROUND_SERVICE
95                             return userState.removeNotification(sbn.getPackageName(), sbn.getKey());
96                         }
97                     }
98 
99                     @Override
100                     public void userStateNotFound(int userId) {
101                         if (DBG) {
102                             Log.w(TAG, String.format(
103                                     "user %d with no known notifications got removeNotification "
104                                             + "for %s",
105                                     sbn.getUserId(), sbn));
106                         }
107                     }
108                 },
109                 false /* don't create */);
110     }
111 
112     /**
113      * @param sbn notification that was just changed in some way
114      */
updateNotification(StatusBarNotification sbn, int newImportance)115     private void updateNotification(StatusBarNotification sbn, int newImportance) {
116         mForegroundServiceController.updateUserState(
117                 sbn.getUserId(),
118                 userState -> {
119                     if (mForegroundServiceController.isDisclosureNotification(sbn)) {
120                         final Bundle extras = sbn.getNotification().extras;
121                         if (extras != null) {
122                             final String[] svcs = extras.getStringArray(
123                                     Notification.EXTRA_FOREGROUND_APPS);
124                             userState.setRunningServices(svcs, sbn.getNotification().when);
125                         }
126                     } else {
127                         userState.removeNotification(sbn.getPackageName(), sbn.getKey());
128                         if (0 != (sbn.getNotification().flags
129                                 & Notification.FLAG_FOREGROUND_SERVICE)) {
130                             if (newImportance > NotificationManager.IMPORTANCE_MIN) {
131                                 userState.addImportantNotification(sbn.getPackageName(),
132                                         sbn.getKey());
133                             }
134                             final Notification.Builder builder =
135                                     Notification.Builder.recoverBuilder(
136                                             mContext, sbn.getNotification());
137                             if (builder.usesStandardHeader()) {
138                                 userState.addStandardLayoutNotification(
139                                         sbn.getPackageName(), sbn.getKey());
140                             }
141                         }
142                     }
143                     return true;
144                 },
145                 true /* create if not found */);
146     }
147 }
148