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