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