• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 package com.android.nfc;
17 
18 import java.util.ArrayList;
19 import java.util.List;
20 
21 import android.app.ActivityManager;
22 import android.app.IActivityManager;
23 import android.app.IProcessObserver;
24 import android.os.RemoteException;
25 import android.util.Log;
26 import android.util.SparseArray;
27 import android.util.SparseBooleanArray;
28 
29 public class ForegroundUtils extends IProcessObserver.Stub {
30     static final boolean DBG = false;
31     private final String TAG = "ForegroundUtils";
32     private final IActivityManager mIActivityManager;
33 
34     private final Object mLock = new Object();
35     // We need to keep track of the individual PIDs per UID,
36     // since a single UID may have multiple processes running
37     // that transition into foreground/background state.
38     private final SparseArray<SparseBooleanArray> mForegroundUidPids =
39             new SparseArray<SparseBooleanArray>();
40     private final SparseArray<List<Callback>> mBackgroundCallbacks =
41             new SparseArray<List<Callback>>();
42 
43     private static class Singleton {
44         private static final ForegroundUtils INSTANCE = new ForegroundUtils();
45     }
46 
ForegroundUtils()47     private ForegroundUtils() {
48         mIActivityManager = ActivityManager.getService();
49         try {
50             mIActivityManager.registerProcessObserver(this);
51         } catch (RemoteException e) {
52             // Should not happen!
53             Log.e(TAG, "ForegroundUtils: could not get IActivityManager");
54         }
55     }
56 
57     public interface Callback {
onUidToBackground(int uid)58         void onUidToBackground(int uid);
59     }
60 
getInstance()61     public static ForegroundUtils getInstance() {
62         return Singleton.INSTANCE;
63     }
64 
65     /**
66      * Checks whether the specified UID has any activities running in the foreground,
67      * and if it does, registers a callback for when that UID no longer has any foreground
68      * activities. This is done atomically, so callers can be ensured that they will
69      * get a callback if this method returns true.
70      *
71      * @param callback Callback to be called
72      * @param uid The UID to be checked
73      * @return true when the UID has an Activity in the foreground and the callback
74      * , false otherwise
75      */
registerUidToBackgroundCallback(Callback callback, int uid)76     public boolean registerUidToBackgroundCallback(Callback callback, int uid) {
77         synchronized (mLock) {
78             if (!isInForegroundLocked(uid)) {
79                 return false;
80             }
81             // This uid is in the foreground; register callback for when it moves
82             // into the background.
83             List<Callback> callbacks = mBackgroundCallbacks.get(uid, new ArrayList<Callback>());
84             callbacks.add(callback);
85             mBackgroundCallbacks.put(uid, callbacks);
86             return true;
87         }
88     }
89 
90     /**
91      * @param uid The UID to be checked
92      * @return whether the UID has any activities running in the foreground
93      */
isInForeground(int uid)94     public boolean isInForeground(int uid) {
95         synchronized (mLock) {
96             return isInForegroundLocked(uid);
97         }
98     }
99 
100     /**
101      * @return a list of UIDs currently in the foreground, or an empty list
102      *         if none are found.
103      */
getForegroundUids()104     public List<Integer> getForegroundUids() {
105         ArrayList<Integer> uids = new ArrayList<Integer>(mForegroundUidPids.size());
106         synchronized (mLock) {
107             for (int i = 0; i < mForegroundUidPids.size(); i++) {
108                 uids.add(mForegroundUidPids.keyAt(i));
109             }
110         }
111         return uids;
112     }
113 
isInForegroundLocked(int uid)114     private boolean isInForegroundLocked(int uid) {
115         if (mForegroundUidPids.get(uid) != null)
116             return true;
117         if (DBG) Log.d(TAG, "Checking UID:" + Integer.toString(uid));
118         try {
119             // If the onForegroundActivitiesChanged() has not yet been called,
120             // check whether the UID is in an active state to use the NFC.
121             return mIActivityManager.isUidActive(uid, NfcApplication.NFC_PROCESS);
122         } catch (RemoteException e) {
123             Log.e(TAG, "ForegroundUtils: could not get isUidActive");
124         }
125         return false;
126     }
127 
handleUidToBackground(int uid)128     private void handleUidToBackground(int uid) {
129         ArrayList<Callback> pendingCallbacks = null;
130         synchronized (mLock) {
131             List<Callback> callbacks = mBackgroundCallbacks.get(uid);
132             if (callbacks != null) {
133                 pendingCallbacks = new ArrayList<Callback>(callbacks);
134                 // Only call them once
135                 mBackgroundCallbacks.remove(uid);
136             }
137         }
138         // Release lock for callbacks
139         if (pendingCallbacks != null) {
140             for (Callback callback : pendingCallbacks) {
141                 callback.onUidToBackground(uid);
142             }
143         }
144     }
145 
146     @Override
onForegroundActivitiesChanged(int pid, int uid, boolean hasForegroundActivities)147     public void onForegroundActivitiesChanged(int pid, int uid,
148             boolean hasForegroundActivities) throws RemoteException {
149         boolean uidToBackground = false;
150         synchronized (mLock) {
151             SparseBooleanArray foregroundPids = mForegroundUidPids.get(uid,
152                     new SparseBooleanArray());
153             if (hasForegroundActivities) {
154                foregroundPids.put(pid, true);
155             } else {
156                foregroundPids.delete(pid);
157             }
158             if (foregroundPids.size() == 0) {
159                 mForegroundUidPids.remove(uid);
160                 uidToBackground = true;
161             } else {
162                 mForegroundUidPids.put(uid, foregroundPids);
163             }
164         }
165         if (uidToBackground) {
166             handleUidToBackground(uid);
167         }
168         if (DBG) {
169             if (DBG) Log.d(TAG, "Foreground changed, PID: " + Integer.toString(pid) + " UID: " +
170                                     Integer.toString(uid) + " foreground: " +
171                                     hasForegroundActivities);
172             synchronized (mLock) {
173                 Log.d(TAG, "Foreground UID/PID combinations:");
174                 for (int i = 0; i < mForegroundUidPids.size(); i++) {
175                     int foregroundUid = mForegroundUidPids.keyAt(i);
176                     SparseBooleanArray foregroundPids = mForegroundUidPids.get(foregroundUid);
177                     if (foregroundPids.size() == 0) {
178                         Log.e(TAG, "No PIDS associated with foreground UID!");
179                     }
180                     for (int j = 0; j < foregroundPids.size(); j++)
181                         Log.d(TAG, "UID: " + Integer.toString(foregroundUid) + " PID: " +
182                                 Integer.toString(foregroundPids.keyAt(j)));
183                 }
184             }
185         }
186     }
187 
188     @Override
onForegroundServicesChanged(int pid, int uid, int fgServiceTypes)189     public void onForegroundServicesChanged(int pid, int uid, int fgServiceTypes) {
190     }
191 
192     @Override
onProcessDied(int pid, int uid)193     public void onProcessDied(int pid, int uid) throws RemoteException {
194         if (DBG) Log.d(TAG, "Process died; UID " + Integer.toString(uid) + " PID " +
195                 Integer.toString(pid));
196         onForegroundActivitiesChanged(pid, uid, false);
197     }
198 }
199