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.app.AppOpsManager;
19 import android.os.Handler;
20 import android.os.UserHandle;
21 import android.service.notification.StatusBarNotification;
22 import android.util.ArraySet;
23 import android.util.SparseArray;
24 
25 import com.android.internal.messages.nano.SystemMessageProto;
26 import com.android.systemui.appops.AppOpsController;
27 import com.android.systemui.dagger.SysUISingleton;
28 import com.android.systemui.dagger.qualifiers.Main;
29 import com.android.systemui.util.Assert;
30 
31 import javax.inject.Inject;
32 
33 /**
34  * Tracks state of foreground services and notifications related to foreground services per user.
35  */
36 @SysUISingleton
37 public class ForegroundServiceController {
38     public static final int[] APP_OPS = new int[] {AppOpsManager.OP_SYSTEM_ALERT_WINDOW};
39 
40     private final SparseArray<ForegroundServicesUserState> mUserServices = new SparseArray<>();
41     private final Object mMutex = new Object();
42     private final Handler mMainHandler;
43 
44     @Inject
ForegroundServiceController( AppOpsController appOpsController, @Main Handler mainHandler)45     public ForegroundServiceController(
46             AppOpsController appOpsController,
47             @Main Handler mainHandler) {
48         mMainHandler = mainHandler;
49         appOpsController.addCallback(APP_OPS, (code, uid, packageName, active) -> {
50             mMainHandler.post(() -> {
51                 onAppOpChanged(code, uid, packageName, active);
52             });
53         });
54     }
55 
56     /**
57      * @return true if this user has services missing notifications and therefore needs a
58      * disclosure notification for running a foreground service.
59      */
isDisclosureNeededForUser(int userId)60     public boolean isDisclosureNeededForUser(int userId) {
61         synchronized (mMutex) {
62             final ForegroundServicesUserState services = mUserServices.get(userId);
63             if (services == null) return false;
64             return services.isDisclosureNeeded();
65         }
66     }
67 
68     /**
69      * @return true if this user/pkg has a missing or custom layout notification and therefore needs
70      * a disclosure notification showing the user which appsOps the app is using.
71      */
isSystemAlertWarningNeeded(int userId, String pkg)72     public boolean isSystemAlertWarningNeeded(int userId, String pkg) {
73         synchronized (mMutex) {
74             final ForegroundServicesUserState services = mUserServices.get(userId);
75             if (services == null) return false;
76             return services.getStandardLayoutKeys(pkg) == null;
77         }
78     }
79 
80     /**
81      * Gets active app ops for this user and package
82      */
83     @Nullable
getAppOps(int userId, String pkg)84     public ArraySet<Integer> getAppOps(int userId, String pkg) {
85         synchronized (mMutex) {
86             final ForegroundServicesUserState services = mUserServices.get(userId);
87             if (services == null) {
88                 return null;
89             }
90             return services.getFeatures(pkg);
91         }
92     }
93 
94     /**
95      * Records active app ops and updates the app op for the pending or visible notifications
96      * with the given parameters.
97      * App Ops are stored in FSC in addition to NotificationEntry in case they change before we
98      * have a notification to tag.
99      * @param appOpCode code for appOp to add/remove
100      * @param uid of user the notification is sent to
101      * @param packageName package that created the notification
102      * @param active whether the appOpCode is active or not
103      */
onAppOpChanged(int appOpCode, int uid, String packageName, boolean active)104     void onAppOpChanged(int appOpCode, int uid, String packageName, boolean active) {
105         Assert.isMainThread();
106 
107         int userId = UserHandle.getUserId(uid);
108         // Record active app ops
109         synchronized (mMutex) {
110             ForegroundServicesUserState userServices = mUserServices.get(userId);
111             if (userServices == null) {
112                 userServices = new ForegroundServicesUserState();
113                 mUserServices.put(userId, userServices);
114             }
115             if (active) {
116                 userServices.addOp(packageName, appOpCode);
117             } else {
118                 userServices.removeOp(packageName, appOpCode);
119             }
120         }
121     }
122 
123     /**
124      * Looks up the {@link ForegroundServicesUserState} for the given {@code userId}, then performs
125      * the given {@link UserStateUpdateCallback} on it.  If no state exists for the user ID, creates
126      * a new one if {@code createIfNotFound} is true, then performs the update on the new state.
127      * If {@code createIfNotFound} is false, no update is performed.
128      *
129      * @return false if no user state was found and none was created; true otherwise.
130      */
updateUserState(int userId, UserStateUpdateCallback updateCallback, boolean createIfNotFound)131     boolean updateUserState(int userId,
132             UserStateUpdateCallback updateCallback,
133             boolean createIfNotFound) {
134         synchronized (mMutex) {
135             ForegroundServicesUserState userState = mUserServices.get(userId);
136             if (userState == null) {
137                 if (createIfNotFound) {
138                     userState = new ForegroundServicesUserState();
139                     mUserServices.put(userId, userState);
140                 } else {
141                     return false;
142                 }
143             }
144             return updateCallback.updateUserState(userState);
145         }
146     }
147 
148     /**
149      * @return true if {@code sbn} is the system-provided disclosure notification containing the
150      * list of running foreground services.
151      */
isDisclosureNotification(StatusBarNotification sbn)152     public boolean isDisclosureNotification(StatusBarNotification sbn) {
153         return sbn.getId() == SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES
154                 && sbn.getTag() == null
155                 && sbn.getPackageName().equals("android");
156     }
157 
158     /**
159      * @return true if sbn is one of the window manager "drawing over other apps" notifications
160      */
isSystemAlertNotification(StatusBarNotification sbn)161     public boolean isSystemAlertNotification(StatusBarNotification sbn) {
162         return sbn.getPackageName().equals("android")
163                 && sbn.getTag() != null
164                 && sbn.getTag().contains("AlertWindowNotification");
165     }
166 
167     /**
168      * Callback provided to {@link #updateUserState(int, UserStateUpdateCallback, boolean)}
169      * to perform the update.
170      */
171     interface UserStateUpdateCallback {
172         /**
173          * Perform update operations on the provided {@code userState}.
174          *
175          * @return true if the update succeeded.
176          */
updateUserState(ForegroundServicesUserState userState)177         boolean updateUserState(ForegroundServicesUserState userState);
178 
179         /** Called if the state was not found and was not created. */
userStateNotFound(int userId)180         default void userStateNotFound(int userId) {
181         }
182     }
183 }
184