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