• 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 androidx.annotation.VisibleForTesting;
25 
26 import java.util.ArrayList;
27 import java.util.List;
28 
29 public class ForegroundUtils implements ActivityManager.OnUidImportanceListener {
30     static final boolean VDBG = false;
31     static final boolean DBG = NfcProperties.debug_enabled().orElse(true);
32     private final String TAG = "ForegroundUtils";
33     private final ActivityManager mActivityManager;
34 
35     private final Object mLock = new Object();
36     private final SparseArray<List<Callback>> mBackgroundCallbacks =
37             new SparseArray<List<Callback>>();
38 
39     private final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
40 
41     private static class Singleton {
42         private static ForegroundUtils sInstance = null;
43     }
44 
45     @VisibleForTesting
ForegroundUtils(ActivityManager am)46     public ForegroundUtils(ActivityManager am) {
47         mActivityManager = am;
48         try {
49             mActivityManager.addOnUidImportanceListener(this,
50                     ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND);
51         } catch (Exception e) {
52             // Should not happen!
53             Log.e(TAG, "ForegroundUtils: could not register UidImportanceListener");
54         }
55     }
56 
57     public interface Callback {
onUidToBackground(int uid)58         void onUidToBackground(int uid);
59     }
60 
61     /**
62      * Get an instance of the ForegroundUtils sinleton
63      *
64      * @param am The ActivityManager instance for initialization
65      * @return the instance
66      */
getInstance(ActivityManager am)67     public static ForegroundUtils getInstance(ActivityManager am) {
68         if (Singleton.sInstance == null) {
69             Singleton.sInstance = new ForegroundUtils(am);
70         }
71         return Singleton.sInstance;
72     }
73 
74     /**
75      * Checks whether the specified UID has any activities running in the foreground,
76      * and if it does, registers a callback for when that UID no longer has any foreground
77      * activities. This is done atomically, so callers can be ensured that they will
78      * get a callback if this method returns true.
79      *
80      * @param callback Callback to be called
81      * @param uid The UID to be checked
82      * @return true when the UID has an Activity in the foreground and the callback
83      * , false otherwise
84      */
registerUidToBackgroundCallback(Callback callback, int uid)85     public boolean registerUidToBackgroundCallback(Callback callback, int uid) {
86         synchronized (mLock) {
87             if (!isInForegroundLocked(uid)) {
88                 return false;
89             }
90             // This uid is in the foreground; register callback for when it moves
91             // into the background.
92             List<Callback> callbacks = mBackgroundCallbacks.get(uid, new ArrayList<Callback>());
93             callbacks.add(callback);
94             mBackgroundCallbacks.put(uid, callbacks);
95             return true;
96         }
97     }
98 
99     /**
100      * @param uid The UID to be checked
101      * @return whether the UID has any activities running in the foreground
102      */
isInForeground(int uid)103     public boolean isInForeground(int uid) {
104         synchronized (mLock) {
105             return isInForegroundLocked(uid);
106         }
107     }
108 
109     /**
110      * @return a list of UIDs currently in the foreground, or an empty list
111      *         if none are found.
112      */
getForegroundUids()113     public List<Integer> getForegroundUids() {
114         ArrayList<Integer> uids = null;
115         synchronized (mLock) {
116             uids = new ArrayList<Integer>(mForegroundUids.size());
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, "isInForegroundLocked: 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 (VDBG) {
163                     Log.d(TAG, "onUidImportance: UID: " + Integer.toString(uid) + " deleted");
164                 }
165                 return;
166             }
167             if (importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
168                 mForegroundUids.put(uid, true);
169             } else {
170                 if (mForegroundUids.get(uid)) {
171                     uidToBackground = true;
172                     mForegroundUids.put(uid, false);
173                 }
174             }
175         }
176         if (uidToBackground) {
177             handleUidToBackground(uid);
178         }
179         if (VDBG) {
180             Log.d(TAG, "onUidImportance: Foreground UID status");
181             synchronized (mLock) {
182                 for (int j = 0; j < mForegroundUids.size(); j++) {
183                     Log.d(TAG, "onUidImportance: UID: " + Integer.toString(mForegroundUids.keyAt(j))
184                             + " is in foreground: " + Boolean.toString(mForegroundUids.valueAt(j)));
185                 }
186             }
187         }
188     }
189     @VisibleForTesting
getBackgroundCallbacks()190     public SparseArray<List<Callback>> getBackgroundCallbacks() {
191         synchronized (mLock) {
192             return mBackgroundCallbacks;
193         }
194     }
195 
196     @VisibleForTesting
clearForegroundlist()197     public void clearForegroundlist() {
198         synchronized (mLock) {
199             mForegroundUids.clear();
200         }
201     }
202 }
203