1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui;
18 
19 import android.util.ArrayMap;
20 import android.util.ArraySet;
21 
22 import java.util.Arrays;
23 
24 /**
25  * Struct to track relevant packages and notifications for a userid's foreground services.
26  */
27 public class ForegroundServicesUserState {
28     // shelf life of foreground services before they go bad
29     private static final long FG_SERVICE_GRACE_MILLIS = 5000;
30 
31     private String[] mRunning = null;
32     private long mServiceStartTime = 0;
33 
34     // package -> sufficiently important posted notification keys that signal an app is
35     // running a foreground service
36     private ArrayMap<String, ArraySet<String>> mImportantNotifications = new ArrayMap<>(1);
37     // package -> standard layout posted notification keys that can display appOps
38     private ArrayMap<String, ArraySet<String>> mStandardLayoutNotifications = new ArrayMap<>(1);
39 
40     // package -> app ops
41     private ArrayMap<String, ArraySet<Integer>> mAppOps = new ArrayMap<>(1);
42 
setRunningServices(String[] pkgs, long serviceStartTime)43     public void setRunningServices(String[] pkgs, long serviceStartTime) {
44         mRunning = pkgs != null ? Arrays.copyOf(pkgs, pkgs.length) : null;
45         mServiceStartTime = serviceStartTime;
46     }
47 
addOp(String pkg, int op)48     public void addOp(String pkg, int op) {
49         if (mAppOps.get(pkg) == null) {
50             mAppOps.put(pkg, new ArraySet<>(3));
51         }
52         mAppOps.get(pkg).add(op);
53     }
54 
removeOp(String pkg, int op)55     public boolean removeOp(String pkg, int op) {
56         final boolean found;
57         final ArraySet<Integer> keys = mAppOps.get(pkg);
58         if (keys == null) {
59             found = false;
60         } else {
61             found = keys.remove(op);
62             if (keys.size() == 0) {
63                 mAppOps.remove(pkg);
64             }
65         }
66         return found;
67     }
68 
addImportantNotification(String pkg, String key)69     public void addImportantNotification(String pkg, String key) {
70         addNotification(mImportantNotifications, pkg, key);
71     }
72 
removeImportantNotification(String pkg, String key)73     public boolean removeImportantNotification(String pkg, String key) {
74         return removeNotification(mImportantNotifications, pkg, key);
75     }
76 
addStandardLayoutNotification(String pkg, String key)77     public void addStandardLayoutNotification(String pkg, String key) {
78         addNotification(mStandardLayoutNotifications, pkg, key);
79     }
80 
removeStandardLayoutNotification(String pkg, String key)81     public boolean removeStandardLayoutNotification(String pkg, String key) {
82         return removeNotification(mStandardLayoutNotifications, pkg, key);
83     }
84 
removeNotification(String pkg, String key)85     public boolean removeNotification(String pkg, String key) {
86         boolean removed = false;
87         removed |= removeImportantNotification(pkg, key);
88         removed |= removeStandardLayoutNotification(pkg, key);
89         return removed;
90     }
91 
addNotification(ArrayMap<String, ArraySet<String>> map, String pkg, String key)92     public void addNotification(ArrayMap<String, ArraySet<String>> map, String pkg,
93             String key) {
94         if (map.get(pkg) == null) {
95             map.put(pkg, new ArraySet<>());
96         }
97         map.get(pkg).add(key);
98     }
99 
removeNotification(ArrayMap<String, ArraySet<String>> map, String pkg, String key)100     public boolean removeNotification(ArrayMap<String, ArraySet<String>> map,
101             String pkg, String key) {
102         final boolean found;
103         final ArraySet<String> keys = map.get(pkg);
104         if (keys == null) {
105             found = false;
106         } else {
107             found = keys.remove(key);
108             if (keys.size() == 0) {
109                 map.remove(pkg);
110             }
111         }
112         return found;
113     }
114 
115     /**
116      * System disclosures for foreground services are required if an app has a foreground service
117      * running AND the app hasn't posted its own notification signalling it is running a
118      * foreground service
119      */
isDisclosureNeeded()120     public boolean isDisclosureNeeded() {
121         if (mRunning != null
122                 && System.currentTimeMillis() - mServiceStartTime
123                 >= FG_SERVICE_GRACE_MILLIS) {
124 
125             for (String pkg : mRunning) {
126                 final ArraySet<String> set = mImportantNotifications.get(pkg);
127                 if (set == null || set.size() == 0) {
128                     return true;
129                 }
130             }
131         }
132         return false;
133     }
134 
getFeatures(String pkg)135     public ArraySet<Integer> getFeatures(String pkg) {
136         return mAppOps.get(pkg);
137     }
138 
139     /**
140      * Gets the notifications with standard layouts associated with this package
141      */
getStandardLayoutKeys(String pkg)142     public ArraySet<String> getStandardLayoutKeys(String pkg) {
143         final ArraySet<String> set = mStandardLayoutNotifications.get(pkg);
144         if (set == null || set.size() == 0) {
145             return null;
146         }
147         return set;
148     }
149 
150     @Override
toString()151     public String toString() {
152         return "UserServices{"
153                 + "mRunning=" + Arrays.toString(mRunning)
154                 + ", mServiceStartTime=" + mServiceStartTime
155                 + ", mImportantNotifications=" + mImportantNotifications
156                 + ", mStandardLayoutNotifications=" + mStandardLayoutNotifications
157                 + '}';
158     }
159 }
160