ForegroundUtils.java revision 34322b73c1e09907cb007e86bae77c744b338cd7
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.nfc;
17
18import java.util.ArrayList;
19import java.util.List;
20
21import android.app.ActivityManagerNative;
22import android.app.IActivityManager;
23import android.app.IProcessObserver;
24import android.os.RemoteException;
25import android.util.Log;
26import android.util.SparseArray;
27
28public class ForegroundUtils extends IProcessObserver.Stub {
29    static final boolean DBG = false;
30    private final String TAG = "ForegroundUtils";
31    private final IActivityManager mIActivityManager;
32
33    private final Object mLock = new Object();
34    private final SparseArray<Boolean> mForegroundUids = new SparseArray<Boolean>(1);
35    private final SparseArray<List<Callback>> mBackgroundCallbacks =
36            new SparseArray<List<Callback>>();
37
38    private static class Singleton {
39        private static final ForegroundUtils INSTANCE = new ForegroundUtils();
40    }
41
42    private ForegroundUtils() {
43        mIActivityManager = ActivityManagerNative.getDefault();
44        try {
45            mIActivityManager.registerProcessObserver(this);
46        } catch (RemoteException e) {
47            // Should not happen!
48            Log.e(TAG, "ForegroundUtils: could not get IActivityManager");
49        }
50    }
51
52    public interface Callback {
53        void onUidToBackground(int uid);
54    }
55
56    public static ForegroundUtils getInstance() {
57        return Singleton.INSTANCE;
58    }
59
60    /**
61     * Checks whether the specified UID has any activities running in the foreground,
62     * and if it does, registers a callback for when that UID no longer has any foreground
63     * activities. This is done atomically, so callers can be ensured that they will
64     * get a callback if this method returns true.
65     *
66     * @param callback Callback to be called
67     * @param uid The UID to be checked
68     * @return true when the UID has an Activity in the foreground and the callback
69     * , false otherwise
70     */
71    public boolean registerUidToBackgroundCallback(Callback callback, int uid) {
72        synchronized (mLock) {
73            if (!isInForegroundLocked(uid)) {
74                return false;
75            }
76            // This uid is in the foreground; register callback for when it moves
77            // into the background.
78            List<Callback> callbacks = mBackgroundCallbacks.get(uid);
79            if (callbacks == null) {
80                callbacks = new ArrayList<Callback>();
81                mBackgroundCallbacks.put(uid, callbacks);
82            }
83            callbacks.add(callback);
84            return true;
85        }
86    }
87
88    /**
89     * @param uid The UID to be checked
90     * @return whether the UID has any activities running in the foreground
91     */
92    public boolean isInForeground(int uid) {
93        synchronized (mLock) {
94            return isInForegroundLocked(uid);
95        }
96    }
97
98    /**
99     * @return the UID of the package currently in the foreground, or -1
100     *         if it can't be determined.
101     */
102    public int getForegroundUid() {
103        synchronized (mLock) {
104            for (int i = 0; i < mForegroundUids.size(); i++) {
105                if (mForegroundUids.valueAt(i).booleanValue()) {
106                    return mForegroundUids.keyAt(i);
107                }
108            }
109        }
110        return -1;
111    }
112
113    private boolean isInForegroundLocked(int uid) {
114        Boolean inForeground = mForegroundUids.get(uid);
115        return inForeground != null ? inForeground.booleanValue() : false;
116    }
117
118    private void handleUidToBackground(int uid) {
119        ArrayList<Callback> pendingCallbacks = null;
120        synchronized (mLock) {
121            List<Callback> callbacks = mBackgroundCallbacks.get(uid);
122            if (callbacks != null) {
123                pendingCallbacks = new ArrayList<Callback>(callbacks);
124                // Only call them once
125                mBackgroundCallbacks.remove(uid);
126            }
127        }
128        // Release lock for callbacks
129        if (pendingCallbacks != null) {
130            for (Callback callback : pendingCallbacks) {
131                callback.onUidToBackground(uid);
132            }
133        }
134    }
135
136    @Override
137    public void onForegroundActivitiesChanged(int pid, int uid,
138            boolean foregroundActivities) throws RemoteException {
139        synchronized (mLock) {
140            mForegroundUids.put(uid, foregroundActivities);
141        }
142        if (DBG) Log.d(TAG, "Foreground changed, PID: " + Integer.toString(pid) + " UID: " +
143                Integer.toString(uid) + " foreground: " + foregroundActivities);
144        if (!foregroundActivities) {
145            handleUidToBackground(uid);
146        }
147    }
148
149
150    @Override
151    public void onProcessDied(int pid, int uid) throws RemoteException {
152        synchronized (mLock) {
153            mForegroundUids.remove(uid);
154        }
155        handleUidToBackground(uid);
156    }
157
158    @Override
159    public void onProcessStateChanged(int pid, int uid, int procState)
160            throws RemoteException {
161        // Don't care
162    }
163}
164