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.ActivityManagerNative; 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 47 private ForegroundUtils() { 48 mIActivityManager = ActivityManagerNative.getDefault(); 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 { 58 void onUidToBackground(int uid); 59 } 60 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 */ 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 */ 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 */ 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 114 private boolean isInForegroundLocked(int uid) { 115 return mForegroundUidPids.get(uid) != null; 116 } 117 118 private void handleUidToBackground(int uid) { 119 ArrayList<Callback> pendingCallbacks = null; 120 synchronized (mLock) { 121 List<Callback> callbacks = mBackgroundCallbacks.get(uid); 122 if (callbacks != null) { 123 pendingCallbacks = new ArrayList<Callback>(callbacks); 124 // Only call them once 125 mBackgroundCallbacks.remove(uid); 126 } 127 } 128 // Release lock for callbacks 129 if (pendingCallbacks != null) { 130 for (Callback callback : pendingCallbacks) { 131 callback.onUidToBackground(uid); 132 } 133 } 134 } 135 136 @Override 137 public void onForegroundActivitiesChanged(int pid, int uid, 138 boolean hasForegroundActivities) throws RemoteException { 139 boolean uidToBackground = false; 140 synchronized (mLock) { 141 SparseBooleanArray foregroundPids = mForegroundUidPids.get(uid, 142 new SparseBooleanArray()); 143 if (hasForegroundActivities) { 144 foregroundPids.put(pid, true); 145 } else { 146 foregroundPids.delete(pid); 147 } 148 if (foregroundPids.size() == 0) { 149 mForegroundUidPids.remove(uid); 150 uidToBackground = true; 151 } else { 152 mForegroundUidPids.put(uid, foregroundPids); 153 } 154 } 155 if (uidToBackground) { 156 handleUidToBackground(uid); 157 } 158 if (DBG) { 159 if (DBG) Log.d(TAG, "Foreground changed, PID: " + Integer.toString(pid) + " UID: " + 160 Integer.toString(uid) + " foreground: " + 161 hasForegroundActivities); 162 synchronized (mLock) { 163 Log.d(TAG, "Foreground UID/PID combinations:"); 164 for (int i = 0; i < mForegroundUidPids.size(); i++) { 165 int foregroundUid = mForegroundUidPids.keyAt(i); 166 SparseBooleanArray foregroundPids = mForegroundUidPids.get(foregroundUid); 167 if (foregroundPids.size() == 0) { 168 Log.e(TAG, "No PIDS associated with foreground UID!"); 169 } 170 for (int j = 0; j < foregroundPids.size(); j++) 171 Log.d(TAG, "UID: " + Integer.toString(foregroundUid) + " PID: " + 172 Integer.toString(foregroundPids.keyAt(j))); 173 } 174 } 175 } 176 } 177 178 179 @Override 180 public void onProcessDied(int pid, int uid) throws RemoteException { 181 if (DBG) Log.d(TAG, "Process died; UID " + Integer.toString(uid) + " PID " + 182 Integer.toString(pid)); 183 onForegroundActivitiesChanged(pid, uid, false); 184 } 185 186 @Override 187 public void onProcessStateChanged(int pid, int uid, int procState) 188 throws RemoteException { 189 // Don't care 190 } 191 } 192