1da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen/* 2da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * Copyright (C) 2014 The Android Open Source Project 3da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * 4da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * Licensed under the Apache License, Version 2.0 (the "License"); 5da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * you may not use this file except in compliance with the License. 6da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * You may obtain a copy of the License at 7da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * 8da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * http://www.apache.org/licenses/LICENSE-2.0 9da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * 10da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * Unless required by applicable law or agreed to in writing, software 11da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * distributed under the License is distributed on an "AS IS" BASIS, 12da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * See the License for the specific language governing permissions and 14da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * limitations under the License. 15da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen */ 16da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenpackage com.android.nfc; 17da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 18da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport java.util.ArrayList; 19da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport java.util.List; 20da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 21da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.app.ActivityManagerNative; 22da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.app.IActivityManager; 23da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.app.IProcessObserver; 24da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.os.RemoteException; 25da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.util.Log; 26da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenimport android.util.SparseArray; 27c4ff3393f54403064b237349277cbf65f3277285Martijn Coenenimport android.util.SparseBooleanArray; 28da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 29da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenpublic class ForegroundUtils extends IProcessObserver.Stub { 3034322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen static final boolean DBG = false; 31da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen private final String TAG = "ForegroundUtils"; 32da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen private final IActivityManager mIActivityManager; 33da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 34da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen private final Object mLock = new Object(); 35c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen // We need to keep track of the individual PIDs per UID, 36c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen // since a single UID may have multiple processes running 37c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen // that transition into foreground/background state. 38c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen private final SparseArray<SparseBooleanArray> mForegroundUidPids = 39c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen new SparseArray<SparseBooleanArray>(); 40da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen private final SparseArray<List<Callback>> mBackgroundCallbacks = 41da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen new SparseArray<List<Callback>>(); 42da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 43da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen private static class Singleton { 44da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen private static final ForegroundUtils INSTANCE = new ForegroundUtils(); 45da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 46da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 47da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen private ForegroundUtils() { 48da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mIActivityManager = ActivityManagerNative.getDefault(); 49da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen try { 50da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mIActivityManager.registerProcessObserver(this); 51da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } catch (RemoteException e) { 52da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Should not happen! 53da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen Log.e(TAG, "ForegroundUtils: could not get IActivityManager"); 54da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 55da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 56da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 57da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public interface Callback { 58da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen void onUidToBackground(int uid); 59da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 60da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 61da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public static ForegroundUtils getInstance() { 62da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return Singleton.INSTANCE; 63da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 64da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 65da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen /** 66da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * Checks whether the specified UID has any activities running in the foreground, 67da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * and if it does, registers a callback for when that UID no longer has any foreground 68da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * activities. This is done atomically, so callers can be ensured that they will 69da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * get a callback if this method returns true. 70da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * 71da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * @param callback Callback to be called 72da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * @param uid The UID to be checked 73da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * @return true when the UID has an Activity in the foreground and the callback 74da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * , false otherwise 75da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen */ 76da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public boolean registerUidToBackgroundCallback(Callback callback, int uid) { 77da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 78da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (!isInForegroundLocked(uid)) { 79da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return false; 80da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 81da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // This uid is in the foreground; register callback for when it moves 82da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // into the background. 83c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen List<Callback> callbacks = mBackgroundCallbacks.get(uid, new ArrayList<Callback>()); 84da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen callbacks.add(callback); 85c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen mBackgroundCallbacks.put(uid, callbacks); 86da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return true; 87da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 88da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 89da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 90da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen /** 91da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * @param uid The UID to be checked 92da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * @return whether the UID has any activities running in the foreground 93da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen */ 94da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public boolean isInForeground(int uid) { 95da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 96da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return isInForegroundLocked(uid); 97da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 98da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 99da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 10034322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen /** 101c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen * @return a list of UIDs currently in the foreground, or an empty list 102c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen * if none are found. 10334322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen */ 104c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen public List<Integer> getForegroundUids() { 105c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen ArrayList<Integer> uids = new ArrayList<Integer>(mForegroundUidPids.size()); 10634322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen synchronized (mLock) { 107c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen for (int i = 0; i < mForegroundUidPids.size(); i++) { 108c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen uids.add(mForegroundUidPids.keyAt(i)); 10934322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen } 11034322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen } 111c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen return uids; 11234322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen } 11334322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen 11434322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen private boolean isInForegroundLocked(int uid) { 115c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen return mForegroundUidPids.get(uid) != null; 116da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 117da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 11834322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen private void handleUidToBackground(int uid) { 119da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen ArrayList<Callback> pendingCallbacks = null; 120da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 121da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen List<Callback> callbacks = mBackgroundCallbacks.get(uid); 122da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (callbacks != null) { 123da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen pendingCallbacks = new ArrayList<Callback>(callbacks); 124da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Only call them once 125da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mBackgroundCallbacks.remove(uid); 126da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 127da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 128da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Release lock for callbacks 129da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (pendingCallbacks != null) { 130da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen for (Callback callback : pendingCallbacks) { 131da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen callback.onUidToBackground(uid); 132da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 133da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 134da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 135da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 136da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen @Override 137da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public void onForegroundActivitiesChanged(int pid, int uid, 138c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen boolean hasForegroundActivities) throws RemoteException { 139c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen boolean uidToBackground = false; 140da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 141c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen SparseBooleanArray foregroundPids = mForegroundUidPids.get(uid, 142c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen new SparseBooleanArray()); 143c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen if (hasForegroundActivities) { 144c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen foregroundPids.put(pid, true); 145c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen } else { 146c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen foregroundPids.delete(pid); 147c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen } 148c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen if (foregroundPids.size() == 0) { 149c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen mForegroundUidPids.remove(uid); 150c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen uidToBackground = true; 151c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen } else { 152c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen mForegroundUidPids.put(uid, foregroundPids); 153c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen } 154da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 155c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen if (uidToBackground) { 156da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen handleUidToBackground(uid); 157da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 158c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen if (DBG) { 159c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen if (DBG) Log.d(TAG, "Foreground changed, PID: " + Integer.toString(pid) + " UID: " + 160c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen Integer.toString(uid) + " foreground: " + 161c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen hasForegroundActivities); 162c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen synchronized (mLock) { 163c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen Log.d(TAG, "Foreground UID/PID combinations:"); 164c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen for (int i = 0; i < mForegroundUidPids.size(); i++) { 165c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen int foregroundUid = mForegroundUidPids.keyAt(i); 166c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen SparseBooleanArray foregroundPids = mForegroundUidPids.get(foregroundUid); 167c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen if (foregroundPids.size() == 0) { 168c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen Log.e(TAG, "No PIDS associated with foreground UID!"); 169c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen } 170c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen for (int j = 0; j < foregroundPids.size(); j++) 171c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen Log.d(TAG, "UID: " + Integer.toString(foregroundUid) + " PID: " + 172c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen Integer.toString(foregroundPids.keyAt(j))); 173c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen } 174c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen } 175c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen } 176da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 177da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 178da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 179da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen @Override 180da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public void onProcessDied(int pid, int uid) throws RemoteException { 181c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen if (DBG) Log.d(TAG, "Process died; UID " + Integer.toString(uid) + " PID " + 182c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen Integer.toString(pid)); 183c4ff3393f54403064b237349277cbf65f3277285Martijn Coenen onForegroundActivitiesChanged(pid, uid, false); 184da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 185da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 186da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen @Override 187da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public void onProcessStateChanged(int pid, int uid, int procState) 188da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen throws RemoteException { 189da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Don't care 190da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 191da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen} 192