Home | History | Annotate | Download | only in nfc
      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