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