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