blob: 82e665bdf5ac8f21b087ad6f53a3f0c19e978f61 [file] [log] [blame]
Dan Sandlerf5aafb92017-05-28 12:18:53 -04001/*
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
15package com.android.systemui;
16
Julia Reynoldsfc640012018-02-21 12:25:27 -050017import android.annotation.Nullable;
Beverly482ad6a2019-11-06 16:57:05 -050018import android.app.AppOpsManager;
Beverlyd30d6d02019-11-08 15:44:21 -050019import android.os.Handler;
Gus Prevasccb54402018-12-28 13:40:50 -050020import android.os.UserHandle;
Dan Sandlerf5aafb92017-05-28 12:18:53 -040021import android.service.notification.StatusBarNotification;
Julia Reynoldsfc640012018-02-21 12:25:27 -050022import android.util.ArraySet;
Gus Prevasccb54402018-12-28 13:40:50 -050023import android.util.SparseArray;
Dan Sandlerf5aafb92017-05-28 12:18:53 -040024
Gus Prevasccb54402018-12-28 13:40:50 -050025import com.android.internal.messages.nano.SystemMessageProto;
Beverly482ad6a2019-11-06 16:57:05 -050026import com.android.systemui.appops.AppOpsController;
Dave Mankoff00e8a2f2019-12-18 16:59:49 -050027import com.android.systemui.dagger.qualifiers.Main;
Beverly201cdd52019-10-18 14:30:46 -040028import com.android.systemui.statusbar.notification.NotificationEntryManager;
29import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Beverly91415692019-11-18 13:17:38 -050030import com.android.systemui.util.Assert;
Gus Prevasccb54402018-12-28 13:40:50 -050031
Gus Prevasccb54402018-12-28 13:40:50 -050032import javax.inject.Inject;
33import javax.inject.Singleton;
34
35/**
Gus Prevaseb4e2e12018-12-28 14:57:59 -050036 * Tracks state of foreground services and notifications related to foreground services per user.
Gus Prevasccb54402018-12-28 13:40:50 -050037 */
38@Singleton
39public class ForegroundServiceController {
Beverly8c80e0c2019-12-03 11:25:39 -050040 public static final int[] APP_OPS = new int[] {AppOpsManager.OP_CAMERA,
Beverly482ad6a2019-11-06 16:57:05 -050041 AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
42 AppOpsManager.OP_RECORD_AUDIO,
43 AppOpsManager.OP_COARSE_LOCATION,
44 AppOpsManager.OP_FINE_LOCATION};
Gus Prevasccb54402018-12-28 13:40:50 -050045
Gus Prevaseb4e2e12018-12-28 14:57:59 -050046 private final SparseArray<ForegroundServicesUserState> mUserServices = new SparseArray<>();
Gus Prevasccb54402018-12-28 13:40:50 -050047 private final Object mMutex = new Object();
Beverly201cdd52019-10-18 14:30:46 -040048 private final NotificationEntryManager mEntryManager;
Beverlyd30d6d02019-11-08 15:44:21 -050049 private final Handler mMainHandler;
Gus Prevasccb54402018-12-28 13:40:50 -050050
51 @Inject
Beverly482ad6a2019-11-06 16:57:05 -050052 public ForegroundServiceController(NotificationEntryManager entryManager,
Dave Mankoff00e8a2f2019-12-18 16:59:49 -050053 AppOpsController appOpsController, @Main Handler mainHandler) {
Beverly201cdd52019-10-18 14:30:46 -040054 mEntryManager = entryManager;
Beverlyd30d6d02019-11-08 15:44:21 -050055 mMainHandler = mainHandler;
56 appOpsController.addCallback(APP_OPS, (code, uid, packageName, active) -> {
57 mMainHandler.post(() -> {
58 onAppOpChanged(code, uid, packageName, active);
59 });
60 });
Gus Prevasccb54402018-12-28 13:40:50 -050061 }
Dan Sandlerf5aafb92017-05-28 12:18:53 -040062
63 /**
Dan Sandlerf5aafb92017-05-28 12:18:53 -040064 * @return true if this user has services missing notifications and therefore needs a
65 * disclosure notification.
66 */
Gus Prevaseb4e2e12018-12-28 14:57:59 -050067 public boolean isDisclosureNeededForUser(int userId) {
Gus Prevasccb54402018-12-28 13:40:50 -050068 synchronized (mMutex) {
Gus Prevaseb4e2e12018-12-28 14:57:59 -050069 final ForegroundServicesUserState services = mUserServices.get(userId);
Gus Prevasccb54402018-12-28 13:40:50 -050070 if (services == null) return false;
Gus Prevaseb4e2e12018-12-28 14:57:59 -050071 return services.isDisclosureNeeded();
Gus Prevasccb54402018-12-28 13:40:50 -050072 }
73 }
Julia Reynoldsfc640012018-02-21 12:25:27 -050074
75 /**
76 * @return true if this user/pkg has a missing or custom layout notification and therefore needs
77 * a disclosure notification for system alert windows.
78 */
Gus Prevasccb54402018-12-28 13:40:50 -050079 public boolean isSystemAlertWarningNeeded(int userId, String pkg) {
80 synchronized (mMutex) {
Gus Prevaseb4e2e12018-12-28 14:57:59 -050081 final ForegroundServicesUserState services = mUserServices.get(userId);
Gus Prevasccb54402018-12-28 13:40:50 -050082 if (services == null) return false;
83 return services.getStandardLayoutKey(pkg) == null;
84 }
85 }
86
87 /**
88 * Returns the key of the foreground service from this package using the standard template,
89 * if one exists.
90 */
91 @Nullable
92 public String getStandardLayoutKey(int userId, String pkg) {
93 synchronized (mMutex) {
Gus Prevaseb4e2e12018-12-28 14:57:59 -050094 final ForegroundServicesUserState services = mUserServices.get(userId);
Gus Prevasccb54402018-12-28 13:40:50 -050095 if (services == null) return null;
96 return services.getStandardLayoutKey(pkg);
97 }
98 }
99
100 /**
101 * Gets active app ops for this user and package
102 */
103 @Nullable
104 public ArraySet<Integer> getAppOps(int userId, String pkg) {
105 synchronized (mMutex) {
Gus Prevaseb4e2e12018-12-28 14:57:59 -0500106 final ForegroundServicesUserState services = mUserServices.get(userId);
Gus Prevasccb54402018-12-28 13:40:50 -0500107 if (services == null) {
108 return null;
109 }
110 return services.getFeatures(pkg);
111 }
112 }
Julia Reynoldsfc640012018-02-21 12:25:27 -0500113
114 /**
Beverly201cdd52019-10-18 14:30:46 -0400115 * Records active app ops and updates the app op for the pending or visible notifications
116 * with the given parameters.
117 * App Ops are stored in FSC in addition to NotificationEntry in case they change before we
118 * have a notification to tag.
119 * @param appOpCode code for appOp to add/remove
120 * @param uid of user the notification is sent to
121 * @param packageName package that created the notification
122 * @param active whether the appOpCode is active or not
Julia Reynoldsfc640012018-02-21 12:25:27 -0500123 */
Beverlyd30d6d02019-11-08 15:44:21 -0500124 void onAppOpChanged(int appOpCode, int uid, String packageName, boolean active) {
Beverly91415692019-11-18 13:17:38 -0500125 Assert.isMainThread();
126
Gus Prevasccb54402018-12-28 13:40:50 -0500127 int userId = UserHandle.getUserId(uid);
Beverly201cdd52019-10-18 14:30:46 -0400128 // Record active app ops
Gus Prevasccb54402018-12-28 13:40:50 -0500129 synchronized (mMutex) {
Gus Prevaseb4e2e12018-12-28 14:57:59 -0500130 ForegroundServicesUserState userServices = mUserServices.get(userId);
Gus Prevasccb54402018-12-28 13:40:50 -0500131 if (userServices == null) {
Gus Prevaseb4e2e12018-12-28 14:57:59 -0500132 userServices = new ForegroundServicesUserState();
Gus Prevasccb54402018-12-28 13:40:50 -0500133 mUserServices.put(userId, userServices);
134 }
135 if (active) {
Beverly201cdd52019-10-18 14:30:46 -0400136 userServices.addOp(packageName, appOpCode);
Gus Prevasccb54402018-12-28 13:40:50 -0500137 } else {
Beverly201cdd52019-10-18 14:30:46 -0400138 userServices.removeOp(packageName, appOpCode);
139 }
140 }
141
Beverlyc29b9692019-12-04 15:05:52 -0500142 // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
143 // ForegroundCoordinator
Beverly201cdd52019-10-18 14:30:46 -0400144 // Update appOp if there's an associated pending or visible notification:
145 final String foregroundKey = getStandardLayoutKey(userId, packageName);
146 if (foregroundKey != null) {
Evan Laird181de622019-10-24 09:53:02 -0400147 final NotificationEntry entry = mEntryManager.getPendingOrActiveNotif(foregroundKey);
Beverly201cdd52019-10-18 14:30:46 -0400148 if (entry != null
149 && uid == entry.getSbn().getUid()
150 && packageName.equals(entry.getSbn().getPackageName())) {
151 boolean changed;
152 synchronized (entry.mActiveAppOps) {
153 if (active) {
154 changed = entry.mActiveAppOps.add(appOpCode);
155 } else {
156 changed = entry.mActiveAppOps.remove(appOpCode);
157 }
158 }
159 if (changed) {
160 mEntryManager.updateNotifications("appOpChanged pkg=" + packageName);
161 }
Gus Prevasccb54402018-12-28 13:40:50 -0500162 }
163 }
164 }
Julia Reynoldsfc640012018-02-21 12:25:27 -0500165
166 /**
Gus Prevaseb4e2e12018-12-28 14:57:59 -0500167 * Looks up the {@link ForegroundServicesUserState} for the given {@code userId}, then performs
168 * the given {@link UserStateUpdateCallback} on it. If no state exists for the user ID, creates
169 * a new one if {@code createIfNotFound} is true, then performs the update on the new state.
170 * If {@code createIfNotFound} is false, no update is performed.
171 *
172 * @return false if no user state was found and none was created; true otherwise.
Julia Reynoldsfc640012018-02-21 12:25:27 -0500173 */
Gus Prevaseb4e2e12018-12-28 14:57:59 -0500174 boolean updateUserState(int userId,
175 UserStateUpdateCallback updateCallback,
176 boolean createIfNotFound) {
Gus Prevasccb54402018-12-28 13:40:50 -0500177 synchronized (mMutex) {
Gus Prevaseb4e2e12018-12-28 14:57:59 -0500178 ForegroundServicesUserState userState = mUserServices.get(userId);
179 if (userState == null) {
180 if (createIfNotFound) {
181 userState = new ForegroundServicesUserState();
182 mUserServices.put(userId, userState);
183 } else {
184 return false;
Gus Prevasccb54402018-12-28 13:40:50 -0500185 }
Gus Prevasccb54402018-12-28 13:40:50 -0500186 }
Gus Prevaseb4e2e12018-12-28 14:57:59 -0500187 return updateCallback.updateUserState(userState);
Gus Prevasccb54402018-12-28 13:40:50 -0500188 }
189 }
190
191 /**
Gus Prevaseb4e2e12018-12-28 14:57:59 -0500192 * @return true if {@code sbn} is the system-provided disclosure notification containing the
193 * list of running foreground services.
Gus Prevasccb54402018-12-28 13:40:50 -0500194 */
Gus Prevaseb4e2e12018-12-28 14:57:59 -0500195 public boolean isDisclosureNotification(StatusBarNotification sbn) {
Gus Prevasccb54402018-12-28 13:40:50 -0500196 return sbn.getId() == SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES
197 && sbn.getTag() == null
198 && sbn.getPackageName().equals("android");
199 }
200
201 /**
202 * @return true if sbn is one of the window manager "drawing over other apps" notifications
203 */
204 public boolean isSystemAlertNotification(StatusBarNotification sbn) {
205 return sbn.getPackageName().equals("android")
206 && sbn.getTag() != null
207 && sbn.getTag().contains("AlertWindowNotification");
208 }
209
210 /**
Gus Prevaseb4e2e12018-12-28 14:57:59 -0500211 * Callback provided to {@link #updateUserState(int, UserStateUpdateCallback, boolean)}
212 * to perform the update.
Gus Prevasccb54402018-12-28 13:40:50 -0500213 */
Gus Prevaseb4e2e12018-12-28 14:57:59 -0500214 interface UserStateUpdateCallback {
215 /**
216 * Perform update operations on the provided {@code userState}.
217 *
218 * @return true if the update succeeded.
219 */
220 boolean updateUserState(ForegroundServicesUserState userState);
Gus Prevasccb54402018-12-28 13:40:50 -0500221
Gus Prevaseb4e2e12018-12-28 14:57:59 -0500222 /** Called if the state was not found and was not created. */
223 default void userStateNotFound(int userId) {
Gus Prevasccb54402018-12-28 13:40:50 -0500224 }
225 }
Dan Sandlerf5aafb92017-05-28 12:18:53 -0400226}