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 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     // package -> sufficiently important posted notification keys
34     private ArrayMap<String, ArraySet<String>> mImportantNotifications = new ArrayMap<>(1);
35     // package -> standard layout posted notification keys
36     private ArrayMap<String, ArraySet<String>> mStandardLayoutNotifications = new ArrayMap<>(1);
37 
38     // package -> app ops
39     private ArrayMap<String, ArraySet<Integer>> mAppOps = new ArrayMap<>(1);
40 
setRunningServices(String[] pkgs, long serviceStartTime)41     public void setRunningServices(String[] pkgs, long serviceStartTime) {
42         mRunning = pkgs != null ? Arrays.copyOf(pkgs, pkgs.length) : null;
43         mServiceStartTime = serviceStartTime;
44     }
45 
addOp(String pkg, int op)46     public void addOp(String pkg, int op) {
47         if (mAppOps.get(pkg) == null) {
48             mAppOps.put(pkg, new ArraySet<>(3));
49         }
50         mAppOps.get(pkg).add(op);
51     }
52 
removeOp(String pkg, int op)53     public boolean removeOp(String pkg, int op) {
54         final boolean found;
55         final ArraySet<Integer> keys = mAppOps.get(pkg);
56         if (keys == null) {
57             found = false;
58         } else {
59             found = keys.remove(op);
60             if (keys.size() == 0) {
61                 mAppOps.remove(pkg);
62             }
63         }
64         return found;
65     }
66 
addImportantNotification(String pkg, String key)67     public void addImportantNotification(String pkg, String key) {
68         addNotification(mImportantNotifications, pkg, key);
69     }
70 
removeImportantNotification(String pkg, String key)71     public boolean removeImportantNotification(String pkg, String key) {
72         return removeNotification(mImportantNotifications, pkg, key);
73     }
74 
addStandardLayoutNotification(String pkg, String key)75     public void addStandardLayoutNotification(String pkg, String key) {
76         addNotification(mStandardLayoutNotifications, pkg, key);
77     }
78 
removeStandardLayoutNotification(String pkg, String key)79     public boolean removeStandardLayoutNotification(String pkg, String key) {
80         return removeNotification(mStandardLayoutNotifications, pkg, key);
81     }
82 
removeNotification(String pkg, String key)83     public boolean removeNotification(String pkg, String key) {
84         boolean removed = false;
85         removed |= removeImportantNotification(pkg, key);
86         removed |= removeStandardLayoutNotification(pkg, key);
87         return removed;
88     }
89 
addNotification(ArrayMap<String, ArraySet<String>> map, String pkg, String key)90     public void addNotification(ArrayMap<String, ArraySet<String>> map, String pkg,
91             String key) {
92         if (map.get(pkg) == null) {
93             map.put(pkg, new ArraySet<>());
94         }
95         map.get(pkg).add(key);
96     }
97 
removeNotification(ArrayMap<String, ArraySet<String>> map, String pkg, String key)98     public boolean removeNotification(ArrayMap<String, ArraySet<String>> map,
99             String pkg, String key) {
100         final boolean found;
101         final ArraySet<String> keys = map.get(pkg);
102         if (keys == null) {
103             found = false;
104         } else {
105             found = keys.remove(key);
106             if (keys.size() == 0) {
107                 map.remove(pkg);
108             }
109         }
110         return found;
111     }
112 
isDisclosureNeeded()113     public boolean isDisclosureNeeded() {
114         if (mRunning != null
115                 && System.currentTimeMillis() - mServiceStartTime
116                 >= FG_SERVICE_GRACE_MILLIS) {
117 
118             for (String pkg : mRunning) {
119                 final ArraySet<String> set = mImportantNotifications.get(pkg);
120                 if (set == null || set.size() == 0) {
121                     return true;
122                 }
123             }
124         }
125         return false;
126     }
127 
getFeatures(String pkg)128     public ArraySet<Integer> getFeatures(String pkg) {
129         return mAppOps.get(pkg);
130     }
131 
getStandardLayoutKey(String pkg)132     public String getStandardLayoutKey(String pkg) {
133         final ArraySet<String> set = mStandardLayoutNotifications.get(pkg);
134         if (set == null || set.size() == 0) {
135             return null;
136         }
137         return set.valueAt(0);
138     }
139 
140     @Override
toString()141     public String toString() {
142         return "UserServices{"
143                 + "mRunning=" + Arrays.toString(mRunning)
144                 + ", mServiceStartTime=" + mServiceStartTime
145                 + ", mImportantNotifications=" + mImportantNotifications
146                 + ", mStandardLayoutNotifications=" + mStandardLayoutNotifications
147                 + '}';
148     }
149 }
150