1 /* 2 * Copyright (C) 2014 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 package com.android.nfc; 17 18 import android.app.ActivityManager; 19 import android.sysprop.NfcProperties; 20 import android.util.Log; 21 import android.util.SparseArray; 22 import android.util.SparseBooleanArray; 23 24 import androidx.annotation.VisibleForTesting; 25 26 import java.util.ArrayList; 27 import java.util.List; 28 29 public class ForegroundUtils implements ActivityManager.OnUidImportanceListener { 30 static final boolean VDBG = false; 31 static final boolean DBG = NfcProperties.debug_enabled().orElse(true); 32 private final String TAG = "ForegroundUtils"; 33 private final ActivityManager mActivityManager; 34 35 private final Object mLock = new Object(); 36 private final SparseArray<List<Callback>> mBackgroundCallbacks = 37 new SparseArray<List<Callback>>(); 38 39 private final SparseBooleanArray mForegroundUids = new SparseBooleanArray(); 40 41 private static class Singleton { 42 private static ForegroundUtils sInstance = null; 43 } 44 45 @VisibleForTesting ForegroundUtils(ActivityManager am)46 public ForegroundUtils(ActivityManager am) { 47 mActivityManager = am; 48 try { 49 mActivityManager.addOnUidImportanceListener(this, 50 ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); 51 } catch (Exception e) { 52 // Should not happen! 53 Log.e(TAG, "ForegroundUtils: could not register UidImportanceListener"); 54 } 55 } 56 57 public interface Callback { onUidToBackground(int uid)58 void onUidToBackground(int uid); 59 } 60 61 /** 62 * Get an instance of the ForegroundUtils sinleton 63 * 64 * @param am The ActivityManager instance for initialization 65 * @return the instance 66 */ getInstance(ActivityManager am)67 public static ForegroundUtils getInstance(ActivityManager am) { 68 if (Singleton.sInstance == null) { 69 Singleton.sInstance = new ForegroundUtils(am); 70 } 71 return Singleton.sInstance; 72 } 73 74 /** 75 * Checks whether the specified UID has any activities running in the foreground, 76 * and if it does, registers a callback for when that UID no longer has any foreground 77 * activities. This is done atomically, so callers can be ensured that they will 78 * get a callback if this method returns true. 79 * 80 * @param callback Callback to be called 81 * @param uid The UID to be checked 82 * @return true when the UID has an Activity in the foreground and the callback 83 * , false otherwise 84 */ registerUidToBackgroundCallback(Callback callback, int uid)85 public boolean registerUidToBackgroundCallback(Callback callback, int uid) { 86 synchronized (mLock) { 87 if (!isInForegroundLocked(uid)) { 88 return false; 89 } 90 // This uid is in the foreground; register callback for when it moves 91 // into the background. 92 List<Callback> callbacks = mBackgroundCallbacks.get(uid, new ArrayList<Callback>()); 93 callbacks.add(callback); 94 mBackgroundCallbacks.put(uid, callbacks); 95 return true; 96 } 97 } 98 99 /** 100 * @param uid The UID to be checked 101 * @return whether the UID has any activities running in the foreground 102 */ isInForeground(int uid)103 public boolean isInForeground(int uid) { 104 synchronized (mLock) { 105 return isInForegroundLocked(uid); 106 } 107 } 108 109 /** 110 * @return a list of UIDs currently in the foreground, or an empty list 111 * if none are found. 112 */ getForegroundUids()113 public List<Integer> getForegroundUids() { 114 ArrayList<Integer> uids = null; 115 synchronized (mLock) { 116 uids = new ArrayList<Integer>(mForegroundUids.size()); 117 for (int i = 0; i < mForegroundUids.size(); i++) { 118 if (mForegroundUids.valueAt(i)) { 119 uids.add(mForegroundUids.keyAt(i)); 120 } 121 } 122 } 123 return uids; 124 } 125 isInForegroundLocked(int uid)126 private boolean isInForegroundLocked(int uid) { 127 if (mForegroundUids.get(uid)) { 128 return true; 129 } 130 if (DBG) Log.d(TAG, "isInForegroundLocked: Checking UID:" + Integer.toString(uid)); 131 // If the onForegroundActivitiesChanged() has not yet been called, 132 // check whether the UID is in an active state to use the NFC. 133 return (mActivityManager.getUidImportance(uid) 134 == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); 135 } 136 handleUidToBackground(int uid)137 private void handleUidToBackground(int uid) { 138 ArrayList<Callback> pendingCallbacks = null; 139 synchronized (mLock) { 140 List<Callback> callbacks = mBackgroundCallbacks.get(uid); 141 if (callbacks != null) { 142 pendingCallbacks = new ArrayList<Callback>(callbacks); 143 // Only call them once 144 mBackgroundCallbacks.remove(uid); 145 } 146 } 147 // Release lock for callbacks 148 if (pendingCallbacks != null) { 149 for (Callback callback : pendingCallbacks) { 150 callback.onUidToBackground(uid); 151 } 152 } 153 } 154 155 @Override onUidImportance(int uid, int importance)156 public void onUidImportance(int uid, int importance) { 157 boolean uidToBackground = false; 158 synchronized (mLock) { 159 if (importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE) { 160 mForegroundUids.delete(uid); 161 mBackgroundCallbacks.remove(uid); 162 if (VDBG) { 163 Log.d(TAG, "onUidImportance: UID: " + Integer.toString(uid) + " deleted"); 164 } 165 return; 166 } 167 if (importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { 168 mForegroundUids.put(uid, true); 169 } else { 170 if (mForegroundUids.get(uid)) { 171 uidToBackground = true; 172 mForegroundUids.put(uid, false); 173 } 174 } 175 } 176 if (uidToBackground) { 177 handleUidToBackground(uid); 178 } 179 if (VDBG) { 180 Log.d(TAG, "onUidImportance: Foreground UID status"); 181 synchronized (mLock) { 182 for (int j = 0; j < mForegroundUids.size(); j++) { 183 Log.d(TAG, "onUidImportance: UID: " + Integer.toString(mForegroundUids.keyAt(j)) 184 + " is in foreground: " + Boolean.toString(mForegroundUids.valueAt(j))); 185 } 186 } 187 } 188 } 189 @VisibleForTesting getBackgroundCallbacks()190 public SparseArray<List<Callback>> getBackgroundCallbacks() { 191 synchronized (mLock) { 192 return mBackgroundCallbacks; 193 } 194 } 195 196 @VisibleForTesting clearForegroundlist()197 public void clearForegroundlist() { 198 synchronized (mLock) { 199 mForegroundUids.clear(); 200 } 201 } 202 } 203