ForegroundUtils.java revision da772582c17e3f5ffe36e4cab3e1ede3cba32060
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    private final String TAG = "ForegroundUtils";
30    private final IActivityManager mIActivityManager;
31
32    private final Object mLock = new Object();
33    private final SparseArray<Boolean> mForegroundUids = new SparseArray<Boolean>(1);
34    private final SparseArray<List<Callback>> mBackgroundCallbacks =
35            new SparseArray<List<Callback>>();
36
37    private static class Singleton {
38        private static final ForegroundUtils INSTANCE = new ForegroundUtils();
39    }
40
41    private ForegroundUtils() {
42        mIActivityManager = ActivityManagerNative.getDefault();
43        try {
44            mIActivityManager.registerProcessObserver(this);
45        } catch (RemoteException e) {
46            // Should not happen!
47            Log.e(TAG, "ForegroundUtils: could not get IActivityManager");
48        }
49    }
50
51    public interface Callback {
52        void onUidToBackground(int uid);
53    }
54
55    public static ForegroundUtils getInstance() {
56        return Singleton.INSTANCE;
57    }
58
59    /**
60     * Checks whether the specified UID has any activities running in the foreground,
61     * and if it does, registers a callback for when that UID no longer has any foreground
62     * activities. This is done atomically, so callers can be ensured that they will
63     * get a callback if this method returns true.
64     *
65     * @param callback Callback to be called
66     * @param uid The UID to be checked
67     * @return true when the UID has an Activity in the foreground and the callback
68     * , false otherwise
69     */
70    public boolean registerUidToBackgroundCallback(Callback callback, int uid) {
71        synchronized (mLock) {
72            if (!isInForegroundLocked(uid)) {
73                return false;
74            }
75            // This uid is in the foreground; register callback for when it moves
76            // into the background.
77            List<Callback> callbacks = mBackgroundCallbacks.get(uid);
78            if (callbacks == null) {
79                callbacks = new ArrayList<Callback>();
80                mBackgroundCallbacks.put(uid, callbacks);
81            }
82            callbacks.add(callback);
83            return true;
84        }
85    }
86
87    /**
88     * @param uid The UID to be checked
89     * @return whether the UID has any activities running in the foreground
90     */
91    public boolean isInForeground(int uid) {
92        synchronized (mLock) {
93            return isInForegroundLocked(uid);
94        }
95    }
96
97    boolean isInForegroundLocked(int uid) {
98        Boolean inForeground = mForegroundUids.get(uid);
99        return inForeground != null ? inForeground.booleanValue() : false;
100    }
101
102    void handleUidToBackground(int uid) {
103        ArrayList<Callback> pendingCallbacks = null;
104        synchronized (mLock) {
105            List<Callback> callbacks = mBackgroundCallbacks.get(uid);
106            if (callbacks != null) {
107                pendingCallbacks = new ArrayList<Callback>(callbacks);
108                // Only call them once
109                mBackgroundCallbacks.remove(uid);
110            }
111        }
112        // Release lock for callbacks
113        if (pendingCallbacks != null) {
114            for (Callback callback : pendingCallbacks) {
115                callback.onUidToBackground(uid);
116            }
117        }
118    }
119
120    @Override
121    public void onForegroundActivitiesChanged(int pid, int uid,
122            boolean foregroundActivities) throws RemoteException {
123        synchronized (mLock) {
124            mForegroundUids.put(uid, foregroundActivities);
125        }
126        if (!foregroundActivities) {
127            handleUidToBackground(uid);
128        }
129    }
130
131
132    @Override
133    public void onProcessDied(int pid, int uid) throws RemoteException {
134        synchronized (mLock) {
135            mForegroundUids.remove(uid);
136        }
137        handleUidToBackground(uid);
138    }
139
140    @Override
141    public void onProcessStateChanged(int pid, int uid, int procState)
142            throws RemoteException {
143        // Don't care
144    }
145}
146