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 java.util.ArrayList; 25 import java.util.List; 26 27 public class ForegroundUtils implements ActivityManager.OnUidImportanceListener { 28 static final boolean DBG = NfcProperties.debug_enabled().orElse(false); 29 private final String TAG = "ForegroundUtils"; 30 private final ActivityManager mActivityManager; 31 32 private final Object mLock = new Object(); 33 // We need to keep track of the individual PIDs per UID, 34 // since a single UID may have multiple processes running 35 // that transition into foreground/background state. 36 private final SparseArray<SparseBooleanArray> mForegroundUidPids = 37 new SparseArray<SparseBooleanArray>(); 38 private final SparseArray<List<Callback>> mBackgroundCallbacks = 39 new SparseArray<List<Callback>>(); 40 41 private final SparseBooleanArray mForegroundUids = new SparseBooleanArray(); 42 43 private static class Singleton { 44 private static ForegroundUtils sInstance = null; 45 } 46 ForegroundUtils(ActivityManager am)47 private ForegroundUtils(ActivityManager am) { 48 mActivityManager = am; 49 try { 50 mActivityManager.addOnUidImportanceListener(this, 51 ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); 52 } catch (Exception e) { 53 // Should not happen! 54 Log.e(TAG, "ForegroundUtils: could not register UidImportanceListener"); 55 } 56 } 57 58 public interface Callback { onUidToBackground(int uid)59 void onUidToBackground(int uid); 60 } 61 62 /** 63 * Get an instance of the ForegroundUtils sinleton 64 * 65 * @param am The ActivityManager instance for initialization 66 * @return the instance 67 */ getInstance(ActivityManager am)68 public static ForegroundUtils getInstance(ActivityManager am) { 69 if (Singleton.sInstance == null) { 70 Singleton.sInstance = new ForegroundUtils(am); 71 } 72 return Singleton.sInstance; 73 } 74 75 /** 76 * Checks whether the specified UID has any activities running in the foreground, 77 * and if it does, registers a callback for when that UID no longer has any foreground 78 * activities. This is done atomically, so callers can be ensured that they will 79 * get a callback if this method returns true. 80 * 81 * @param callback Callback to be called 82 * @param uid The UID to be checked 83 * @return true when the UID has an Activity in the foreground and the callback 84 * , false otherwise 85 */ registerUidToBackgroundCallback(Callback callback, int uid)86 public boolean registerUidToBackgroundCallback(Callback callback, int uid) { 87 synchronized (mLock) { 88 if (!isInForegroundLocked(uid)) { 89 return false; 90 } 91 // This uid is in the foreground; register callback for when it moves 92 // into the background. 93 List<Callback> callbacks = mBackgroundCallbacks.get(uid, new ArrayList<Callback>()); 94 callbacks.add(callback); 95 mBackgroundCallbacks.put(uid, callbacks); 96 return true; 97 } 98 } 99 100 /** 101 * @param uid The UID to be checked 102 * @return whether the UID has any activities running in the foreground 103 */ isInForeground(int uid)104 public boolean isInForeground(int uid) { 105 synchronized (mLock) { 106 return isInForegroundLocked(uid); 107 } 108 } 109 110 /** 111 * @return a list of UIDs currently in the foreground, or an empty list 112 * if none are found. 113 */ getForegroundUids()114 public List<Integer> getForegroundUids() { 115 ArrayList<Integer> uids = new ArrayList<Integer>(mForegroundUids.size()); 116 synchronized (mLock) { 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, "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 (DBG) Log.d(TAG, "UID: " + Integer.toString(uid) + " deleted."); 163 return; 164 } 165 if (importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { 166 mForegroundUids.put(uid, true); 167 } else { 168 if (mForegroundUids.get(uid)) { 169 uidToBackground = true; 170 mForegroundUids.put(uid, false); 171 } 172 } 173 } 174 if (uidToBackground) { 175 handleUidToBackground(uid); 176 } 177 if (DBG) { 178 Log.d(TAG, "Foreground UID status:"); 179 synchronized (mLock) { 180 for (int j = 0; j < mForegroundUids.size(); j++) { 181 Log.d(TAG, "UID: " + Integer.toString(mForegroundUids.keyAt(j)) 182 + " is in foreground: " + Boolean.toString(mForegroundUids.valueAt(j))); 183 } 184 } 185 } 186 } 187 } 188