ForegroundUtils.java revision 34322b73c1e09907cb007e86bae77c744b338cd7
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; 27da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 28da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenenpublic class ForegroundUtils extends IProcessObserver.Stub { 2934322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen static final boolean DBG = false; 30da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen private final String TAG = "ForegroundUtils"; 31da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen private final IActivityManager mIActivityManager; 32da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 33da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen private final Object mLock = new Object(); 34da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen private final SparseArray<Boolean> mForegroundUids = new SparseArray<Boolean>(1); 35da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen private final SparseArray<List<Callback>> mBackgroundCallbacks = 36da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen new SparseArray<List<Callback>>(); 37da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 38da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen private static class Singleton { 39da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen private static final ForegroundUtils INSTANCE = new ForegroundUtils(); 40da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 41da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 42da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen private ForegroundUtils() { 43da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mIActivityManager = ActivityManagerNative.getDefault(); 44da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen try { 45da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mIActivityManager.registerProcessObserver(this); 46da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } catch (RemoteException e) { 47da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Should not happen! 48da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen Log.e(TAG, "ForegroundUtils: could not get IActivityManager"); 49da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 50da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 51da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 52da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public interface Callback { 53da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen void onUidToBackground(int uid); 54da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 55da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 56da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public static ForegroundUtils getInstance() { 57da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return Singleton.INSTANCE; 58da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 59da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 60da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen /** 61da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * Checks whether the specified UID has any activities running in the foreground, 62da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * and if it does, registers a callback for when that UID no longer has any foreground 63da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * activities. This is done atomically, so callers can be ensured that they will 64da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * get a callback if this method returns true. 65da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * 66da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * @param callback Callback to be called 67da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * @param uid The UID to be checked 68da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * @return true when the UID has an Activity in the foreground and the callback 69da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * , false otherwise 70da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen */ 71da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public boolean registerUidToBackgroundCallback(Callback callback, int uid) { 72da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 73da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (!isInForegroundLocked(uid)) { 74da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return false; 75da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 76da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // This uid is in the foreground; register callback for when it moves 77da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // into the background. 78da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen List<Callback> callbacks = mBackgroundCallbacks.get(uid); 79da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (callbacks == null) { 80da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen callbacks = new ArrayList<Callback>(); 81da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mBackgroundCallbacks.put(uid, callbacks); 82da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 83da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen callbacks.add(callback); 84da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return true; 85da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 86da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 87da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 88da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen /** 89da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * @param uid The UID to be checked 90da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen * @return whether the UID has any activities running in the foreground 91da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen */ 92da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public boolean isInForeground(int uid) { 93da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 94da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return isInForegroundLocked(uid); 95da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 96da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 97da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 9834322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen /** 9934322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen * @return the UID of the package currently in the foreground, or -1 10034322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen * if it can't be determined. 10134322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen */ 10234322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen public int getForegroundUid() { 10334322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen synchronized (mLock) { 10434322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen for (int i = 0; i < mForegroundUids.size(); i++) { 10534322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen if (mForegroundUids.valueAt(i).booleanValue()) { 10634322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen return mForegroundUids.keyAt(i); 10734322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen } 10834322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen } 10934322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen } 11034322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen return -1; 11134322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen } 11234322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen 11334322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen private boolean isInForegroundLocked(int uid) { 114da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen Boolean inForeground = mForegroundUids.get(uid); 115da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen return inForeground != null ? inForeground.booleanValue() : false; 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, 138da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen boolean foregroundActivities) throws RemoteException { 139da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 140da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mForegroundUids.put(uid, foregroundActivities); 141da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 14234322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen if (DBG) Log.d(TAG, "Foreground changed, PID: " + Integer.toString(pid) + " UID: " + 14334322b73c1e09907cb007e86bae77c744b338cd7Martijn Coenen Integer.toString(uid) + " foreground: " + foregroundActivities); 144da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen if (!foregroundActivities) { 145da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen handleUidToBackground(uid); 146da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 147da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 148da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 149da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 150da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen @Override 151da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public void onProcessDied(int pid, int uid) throws RemoteException { 152da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen synchronized (mLock) { 153da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen mForegroundUids.remove(uid); 154da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 155da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen handleUidToBackground(uid); 156da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 157da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen 158da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen @Override 159da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen public void onProcessStateChanged(int pid, int uid, int procState) 160da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen throws RemoteException { 161da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen // Don't care 162da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen } 163da772582c17e3f5ffe36e4cab3e1ede3cba32060Martijn Coenen} 164