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