Home | History | Annotate | Download | only in systemui
      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
     38     public ForegroundServiceController() {
     39     }
     40 
     41     /**
     42      * @return true if this user has services missing notifications and therefore needs a
     43      * disclosure notification.
     44      */
     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      */
     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
     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
     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      */
     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      */
    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      */
    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      */
    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          */
    166         boolean updateUserState(ForegroundServicesUserState userState);
    167 
    168         /** Called if the state was not found and was not created. */
    169         default void userStateNotFound(int userId) {
    170         }
    171     }
    172 }
    173