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