1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.systemui;
16 
17 import android.annotation.Nullable;
18 import android.os.UserHandle;
19 import android.service.notification.StatusBarNotification;
20 import android.util.ArraySet;
21 import android.util.SparseArray;
22 
23 import com.android.internal.messages.nano.SystemMessageProto;
24 
25 import javax.inject.Inject;
26 import javax.inject.Singleton;
27 
28 /**
29  * Tracks state of foreground services and notifications related to foreground services per user.
30  */
31 @Singleton
32 public class ForegroundServiceController {
33 
34     private final SparseArray<ForegroundServicesUserState> mUserServices = new SparseArray<>();
35     private final Object mMutex = new Object();
36 
37     @Inject
ForegroundServiceController()38     public ForegroundServiceController() {
39     }
40 
41     /**
42      * @return true if this user has services missing notifications and therefore needs a
43      * disclosure notification.
44      */
isDisclosureNeededForUser(int userId)45     public boolean isDisclosureNeededForUser(int userId) {
46         synchronized (mMutex) {
47             final ForegroundServicesUserState services = mUserServices.get(userId);
48             if (services == null) return false;
49             return services.isDisclosureNeeded();
50         }
51     }
52 
53     /**
54      * @return true if this user/pkg has a missing or custom layout notification and therefore needs
55      * a disclosure notification for system alert windows.
56      */
isSystemAlertWarningNeeded(int userId, String pkg)57     public boolean isSystemAlertWarningNeeded(int userId, String pkg) {
58         synchronized (mMutex) {
59             final ForegroundServicesUserState services = mUserServices.get(userId);
60             if (services == null) return false;
61             return services.getStandardLayoutKey(pkg) == null;
62         }
63     }
64 
65     /**
66      * Returns the key of the foreground service from this package using the standard template,
67      * if one exists.
68      */
69     @Nullable
getStandardLayoutKey(int userId, String pkg)70     public String getStandardLayoutKey(int userId, String pkg) {
71         synchronized (mMutex) {
72             final ForegroundServicesUserState services = mUserServices.get(userId);
73             if (services == null) return null;
74             return services.getStandardLayoutKey(pkg);
75         }
76     }
77 
78     /**
79      * Gets active app ops for this user and package
80      */
81     @Nullable
getAppOps(int userId, String pkg)82     public ArraySet<Integer> getAppOps(int userId, String pkg) {
83         synchronized (mMutex) {
84             final ForegroundServicesUserState services = mUserServices.get(userId);
85             if (services == null) {
86                 return null;
87             }
88             return services.getFeatures(pkg);
89         }
90     }
91 
92     /**
93      * Records active app ops. App Ops are stored in FSC in addition to NotificationData in
94      * case they change before we have a notification to tag.
95      */
onAppOpChanged(int code, int uid, String packageName, boolean active)96     public void onAppOpChanged(int code, int uid, String packageName, boolean active) {
97         int userId = UserHandle.getUserId(uid);
98         synchronized (mMutex) {
99             ForegroundServicesUserState userServices = mUserServices.get(userId);
100             if (userServices == null) {
101                 userServices = new ForegroundServicesUserState();
102                 mUserServices.put(userId, userServices);
103             }
104             if (active) {
105                 userServices.addOp(packageName, code);
106             } else {
107                 userServices.removeOp(packageName, code);
108             }
109         }
110     }
111 
112     /**
113      * Looks up the {@link ForegroundServicesUserState} for the given {@code userId}, then performs
114      * the given {@link UserStateUpdateCallback} on it.  If no state exists for the user ID, creates
115      * a new one if {@code createIfNotFound} is true, then performs the update on the new state.
116      * If {@code createIfNotFound} is false, no update is performed.
117      *
118      * @return false if no user state was found and none was created; true otherwise.
119      */
updateUserState(int userId, UserStateUpdateCallback updateCallback, boolean createIfNotFound)120     boolean updateUserState(int userId,
121             UserStateUpdateCallback updateCallback,
122             boolean createIfNotFound) {
123         synchronized (mMutex) {
124             ForegroundServicesUserState userState = mUserServices.get(userId);
125             if (userState == null) {
126                 if (createIfNotFound) {
127                     userState = new ForegroundServicesUserState();
128                     mUserServices.put(userId, userState);
129                 } else {
130                     return false;
131                 }
132             }
133             return updateCallback.updateUserState(userState);
134         }
135     }
136 
137     /**
138      * @return true if {@code sbn} is the system-provided disclosure notification containing the
139      * list of running foreground services.
140      */
isDisclosureNotification(StatusBarNotification sbn)141     public boolean isDisclosureNotification(StatusBarNotification sbn) {
142         return sbn.getId() == SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES
143                 && sbn.getTag() == null
144                 && sbn.getPackageName().equals("android");
145     }
146 
147     /**
148      * @return true if sbn is one of the window manager "drawing over other apps" notifications
149      */
isSystemAlertNotification(StatusBarNotification sbn)150     public boolean isSystemAlertNotification(StatusBarNotification sbn) {
151         return sbn.getPackageName().equals("android")
152                 && sbn.getTag() != null
153                 && sbn.getTag().contains("AlertWindowNotification");
154     }
155 
156     /**
157      * Callback provided to {@link #updateUserState(int, UserStateUpdateCallback, boolean)}
158      * to perform the update.
159      */
160     interface UserStateUpdateCallback {
161         /**
162          * Perform update operations on the provided {@code userState}.
163          *
164          * @return true if the update succeeded.
165          */
updateUserState(ForegroundServicesUserState userState)166         boolean updateUserState(ForegroundServicesUserState userState);
167 
168         /** Called if the state was not found and was not created. */
userStateNotFound(int userId)169         default void userStateNotFound(int userId) {
170         }
171     }
172 }
173