ForegroundUtils.java revision c4ff3393f54403064b237349277cbf65f3277285
16142a54baae3289f734947c6b5375b12eb0fb722Chris Banes/*
26142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Copyright (C) 2014 The Android Open Source Project
36142a54baae3289f734947c6b5375b12eb0fb722Chris Banes *
46142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Licensed under the Apache License, Version 2.0 (the "License");
56142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * you may not use this file except in compliance with the License.
66142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * You may obtain a copy of the License at
76142a54baae3289f734947c6b5375b12eb0fb722Chris Banes *
86142a54baae3289f734947c6b5375b12eb0fb722Chris Banes *      http://www.apache.org/licenses/LICENSE-2.0
96142a54baae3289f734947c6b5375b12eb0fb722Chris Banes *
106142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Unless required by applicable law or agreed to in writing, software
116142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * distributed under the License is distributed on an "AS IS" BASIS,
126142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * See the License for the specific language governing permissions and
146142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * limitations under the License.
156142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */
166142a54baae3289f734947c6b5375b12eb0fb722Chris Banespackage com.android.nfc;
176142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
186142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport java.util.ArrayList;
196142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport java.util.List;
206142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
216142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.app.ActivityManagerNative;
226142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.app.IActivityManager;
23c9b31694a631e135b85b2cf36a8e455e196a68e2Chris Banesimport android.app.IProcessObserver;
246142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.os.RemoteException;
256142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.util.Log;
266142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.util.SparseArray;
276142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.util.SparseBooleanArray;
286142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
296142a54baae3289f734947c6b5375b12eb0fb722Chris Banespublic class ForegroundUtils extends IProcessObserver.Stub {
306142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    static final boolean DBG = false;
316142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private final String TAG = "ForegroundUtils";
326142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private final IActivityManager mIActivityManager;
336142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
346142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private final Object mLock = new Object();
356142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    // We need to keep track of the individual PIDs per UID,
366142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    // since a single UID may have multiple processes running
376142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    // that transition into foreground/background state.
386142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private final SparseArray<SparseBooleanArray> mForegroundUidPids =
396142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            new SparseArray<SparseBooleanArray>();
406142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private final SparseArray<List<Callback>> mBackgroundCallbacks =
416142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            new SparseArray<List<Callback>>();
426142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
436142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private static class Singleton {
446142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        private static final ForegroundUtils INSTANCE = new ForegroundUtils();
456142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    }
466142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
476142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private ForegroundUtils() {
486142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        mIActivityManager = ActivityManagerNative.getDefault();
496142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        try {
506142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            mIActivityManager.registerProcessObserver(this);
516142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        } catch (RemoteException e) {
526142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            // Should not happen!
536142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            Log.e(TAG, "ForegroundUtils: could not get IActivityManager");
546142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        }
556142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    }
566142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
576142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public interface Callback {
586142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        void onUidToBackground(int uid);
596142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    }
606142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
616142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public static ForegroundUtils getInstance() {
626142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        return Singleton.INSTANCE;
636142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    }
646142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
656142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
666142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * Checks whether the specified UID has any activities running in the foreground,
676142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * and if it does, registers a callback for when that UID no longer has any foreground
686142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * activities. This is done atomically, so callers can be ensured that they will
696142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * get a callback if this method returns true.
706142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     *
716142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @param callback Callback to be called
726142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @param uid The UID to be checked
736142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @return true when the UID has an Activity in the foreground and the callback
746142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * , false otherwise
756142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
766142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public boolean registerUidToBackgroundCallback(Callback callback, int uid) {
776142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        synchronized (mLock) {
786142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            if (!isInForegroundLocked(uid)) {
796142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                return false;
806142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            }
816142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            // This uid is in the foreground; register callback for when it moves
826142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            // into the background.
836142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            List<Callback> callbacks = mBackgroundCallbacks.get(uid, new ArrayList<Callback>());
846142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            callbacks.add(callback);
856142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            mBackgroundCallbacks.put(uid, callbacks);
866142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            return true;
876142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        }
886142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    }
896142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
906142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
916142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @param uid The UID to be checked
926142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @return whether the UID has any activities running in the foreground
936142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
946142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public boolean isInForeground(int uid) {
956142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        synchronized (mLock) {
966142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            return isInForegroundLocked(uid);
976142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        }
986142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    }
996142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
1006142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    /**
1016142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     * @return a list of UIDs currently in the foreground, or an empty list
1026142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     *         if none are found.
1036142a54baae3289f734947c6b5375b12eb0fb722Chris Banes     */
1046142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public List<Integer> getForegroundUids() {
1056142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        ArrayList<Integer> uids = new ArrayList<Integer>(mForegroundUidPids.size());
1066142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        synchronized (mLock) {
1076142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            for (int i = 0; i < mForegroundUidPids.size(); i++) {
1086142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                uids.add(mForegroundUidPids.keyAt(i));
1096142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            }
1106142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        }
1116142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        return uids;
1126142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    }
1136142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
1146142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private boolean isInForegroundLocked(int uid) {
1156142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        return mForegroundUidPids.get(uid) != null;
1166142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    }
1176142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
1186142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private void handleUidToBackground(int uid) {
1196142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        ArrayList<Callback> pendingCallbacks = null;
1206142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        synchronized (mLock) {
1216142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            List<Callback> callbacks = mBackgroundCallbacks.get(uid);
1226142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            if (callbacks != null) {
1236142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                pendingCallbacks = new ArrayList<Callback>(callbacks);
1246142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                // Only call them once
1256142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                mBackgroundCallbacks.remove(uid);
1266142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            }
127c9b31694a631e135b85b2cf36a8e455e196a68e2Chris Banes        }
128c9b31694a631e135b85b2cf36a8e455e196a68e2Chris Banes        // Release lock for callbacks
129c9b31694a631e135b85b2cf36a8e455e196a68e2Chris Banes        if (pendingCallbacks != null) {
130c9b31694a631e135b85b2cf36a8e455e196a68e2Chris Banes            for (Callback callback : pendingCallbacks) {
1316142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                callback.onUidToBackground(uid);
1326142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            }
1336142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        }
1346142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    }
1356142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
1366142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    @Override
1376142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public void onForegroundActivitiesChanged(int pid, int uid,
1386142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            boolean hasForegroundActivities) throws RemoteException {
1396142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        boolean uidToBackground = false;
1406142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        synchronized (mLock) {
1416142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            SparseBooleanArray foregroundPids = mForegroundUidPids.get(uid,
1426142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                    new SparseBooleanArray());
1436142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            if (hasForegroundActivities) {
1446142a54baae3289f734947c6b5375b12eb0fb722Chris Banes               foregroundPids.put(pid, true);
1456142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            } else {
1466142a54baae3289f734947c6b5375b12eb0fb722Chris Banes               foregroundPids.delete(pid);
1476142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            }
1486142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            if (foregroundPids.size() == 0) {
1496142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                mForegroundUidPids.remove(uid);
1506142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                uidToBackground = true;
1516142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            } else {
1526142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                mForegroundUidPids.put(uid, foregroundPids);
1536142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            }
1546142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        }
1556142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (uidToBackground) {
1566142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            handleUidToBackground(uid);
1576142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        }
1586142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (DBG) {
1596142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            if (DBG) Log.d(TAG, "Foreground changed, PID: " + Integer.toString(pid) + " UID: " +
1606142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                                    Integer.toString(uid) + " foreground: " +
1616142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                                    hasForegroundActivities);
1626142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            synchronized (mLock) {
1636142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                Log.d(TAG, "Foreground UID/PID combinations:");
1646142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                for (int i = 0; i < mForegroundUidPids.size(); i++) {
1656142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                    int foregroundUid = mForegroundUidPids.keyAt(i);
1666142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                    SparseBooleanArray foregroundPids = mForegroundUidPids.get(foregroundUid);
1676142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                    if (foregroundPids.size() == 0) {
1686142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                        Log.e(TAG, "No PIDS associated with foreground UID!");
1696142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                    }
1706142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                    for (int j = 0; j < foregroundPids.size(); j++)
1716142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                        Log.d(TAG, "UID: " + Integer.toString(foregroundUid) + " PID: " +
1726142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                                Integer.toString(foregroundPids.keyAt(j)));
1736142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                }
1746142a54baae3289f734947c6b5375b12eb0fb722Chris Banes            }
1756142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        }
1766142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    }
1776142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
1786142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
1796142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    @Override
1806142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public void onProcessDied(int pid, int uid) throws RemoteException {
1816142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        if (DBG) Log.d(TAG, "Process died; UID " + Integer.toString(uid) + " PID " +
1826142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                Integer.toString(pid));
1836142a54baae3289f734947c6b5375b12eb0fb722Chris Banes        onForegroundActivitiesChanged(pid, uid, false);
1846142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    }
1856142a54baae3289f734947c6b5375b12eb0fb722Chris Banes
1866142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    @Override
1876142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    public void onProcessStateChanged(int pid, int uid, int procState)
188d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            throws RemoteException {
189d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        // Don't care
190d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    }
191d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes}
192d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes