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.statusbar.notification;
18 
19 import android.Manifest;
20 import android.app.AppGlobals;
21 import android.app.Notification;
22 import android.content.pm.IPackageManager;
23 import android.content.pm.PackageManager;
24 import android.os.RemoteException;
25 import android.service.notification.StatusBarNotification;
26 
27 import com.android.internal.annotations.VisibleForTesting;
28 import com.android.systemui.Dependency;
29 import com.android.systemui.ForegroundServiceController;
30 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
31 import com.android.systemui.statusbar.notification.collection.NotificationData;
32 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
33 import com.android.systemui.statusbar.phone.NotificationGroupManager;
34 import com.android.systemui.statusbar.phone.ShadeController;
35 import com.android.systemui.statusbar.phone.StatusBar;
36 
37 import javax.inject.Inject;
38 import javax.inject.Singleton;
39 
40 /** Component which manages the various reasons a notification might be filtered out. */
41 @Singleton
42 public class NotificationFilter {
43 
44     private final NotificationGroupManager mGroupManager = Dependency.get(
45             NotificationGroupManager.class);
46 
47     private NotificationData.KeyguardEnvironment mEnvironment;
48     private ShadeController mShadeController;
49     private ForegroundServiceController mFsc;
50     private NotificationLockscreenUserManager mUserManager;
51 
52     @Inject
NotificationFilter()53     public NotificationFilter() {}
54 
getEnvironment()55     private NotificationData.KeyguardEnvironment getEnvironment() {
56         if (mEnvironment == null) {
57             mEnvironment = Dependency.get(NotificationData.KeyguardEnvironment.class);
58         }
59         return mEnvironment;
60     }
61 
getShadeController()62     private ShadeController getShadeController() {
63         if (mShadeController == null) {
64             mShadeController = Dependency.get(ShadeController.class);
65         }
66         return mShadeController;
67     }
68 
getFsc()69     private ForegroundServiceController getFsc() {
70         if (mFsc == null) {
71             mFsc = Dependency.get(ForegroundServiceController.class);
72         }
73         return mFsc;
74     }
75 
getUserManager()76     private NotificationLockscreenUserManager getUserManager() {
77         if (mUserManager == null) {
78             mUserManager = Dependency.get(NotificationLockscreenUserManager.class);
79         }
80         return mUserManager;
81     }
82 
83 
84     /**
85      * @return true if the provided notification should NOT be shown right now.
86      */
shouldFilterOut(NotificationEntry entry)87     public boolean shouldFilterOut(NotificationEntry entry) {
88         final StatusBarNotification sbn = entry.notification;
89         if (!(getEnvironment().isDeviceProvisioned()
90                 || showNotificationEvenIfUnprovisioned(sbn))) {
91             return true;
92         }
93 
94         if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
95             return true;
96         }
97 
98         if (getUserManager().isLockscreenPublicMode(sbn.getUserId())
99                 && (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
100                         || getUserManager().shouldHideNotifications(sbn.getUserId())
101                         || getUserManager().shouldHideNotifications(sbn.getKey()))) {
102             return true;
103         }
104 
105         if (getShadeController().isDozing() && entry.shouldSuppressAmbient()) {
106             return true;
107         }
108 
109         if (!getShadeController().isDozing() && entry.shouldSuppressNotificationList()) {
110             return true;
111         }
112 
113         if (entry.suspended) {
114             return true;
115         }
116 
117         if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
118                 && mGroupManager.isChildInGroupWithSummary(sbn)) {
119             return true;
120         }
121 
122         if (getFsc().isDisclosureNotification(sbn)
123                 && !getFsc().isDisclosureNeededForUser(sbn.getUserId())) {
124             // this is a foreground-service disclosure for a user that does not need to show one
125             return true;
126         }
127         if (getFsc().isSystemAlertNotification(sbn)) {
128             final String[] apps = sbn.getNotification().extras.getStringArray(
129                     Notification.EXTRA_FOREGROUND_APPS);
130             if (apps != null && apps.length >= 1) {
131                 if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
132                     return true;
133                 }
134             }
135         }
136         return false;
137     }
138 
139     // Q: What kinds of notifications should show during setup?
140     // A: Almost none! Only things coming from packages with permission
141     // android.permission.NOTIFICATION_DURING_SETUP that also have special "kind" tags marking them
142     // as relevant for setup (see below).
showNotificationEvenIfUnprovisioned(StatusBarNotification sbn)143     private static boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
144         return showNotificationEvenIfUnprovisioned(AppGlobals.getPackageManager(), sbn);
145     }
146 
147     @VisibleForTesting
showNotificationEvenIfUnprovisioned(IPackageManager packageManager, StatusBarNotification sbn)148     static boolean showNotificationEvenIfUnprovisioned(IPackageManager packageManager,
149             StatusBarNotification sbn) {
150         return checkUidPermission(packageManager, Manifest.permission.NOTIFICATION_DURING_SETUP,
151                 sbn.getUid()) == PackageManager.PERMISSION_GRANTED
152                 && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
153     }
154 
checkUidPermission(IPackageManager packageManager, String permission, int uid)155     private static int checkUidPermission(IPackageManager packageManager, String permission,
156             int uid) {
157         try {
158             return packageManager.checkUidPermission(permission, uid);
159         } catch (RemoteException e) {
160             throw e.rethrowFromSystemServer();
161         }
162     }
163 }
164