From c48b7447d282bf9b00575915b59af08dca4d9ba7 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Mon, 23 May 2016 12:30:43 +0200 Subject: Verify setForegroundDispatch caller is in foreground. (DO NOT MERGE) Bug: 28300969 Change-Id: Ibcc2fc384a91a452d378c9ff9632ec110bd4e063 --- AndroidManifest.xml | 1 + src/com/android/nfc/ForegroundUtils.java | 191 +++++++++++++++++++++++++++++++ src/com/android/nfc/NfcService.java | 11 ++ 3 files changed, 203 insertions(+) create mode 100644 src/com/android/nfc/ForegroundUtils.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 95fedff9..6aa12501 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -40,6 +40,7 @@ + mForegroundUidPids = + new SparseArray(); + private final SparseArray> mBackgroundCallbacks = + new SparseArray>(); + + private static class Singleton { + private static final ForegroundUtils INSTANCE = new ForegroundUtils(); + } + + private ForegroundUtils() { + mIActivityManager = ActivityManagerNative.getDefault(); + try { + mIActivityManager.registerProcessObserver(this); + } catch (RemoteException e) { + // Should not happen! + Log.e(TAG, "ForegroundUtils: could not get IActivityManager"); + } + } + + public interface Callback { + void onUidToBackground(int uid); + } + + public static ForegroundUtils getInstance() { + return Singleton.INSTANCE; + } + + /** + * Checks whether the specified UID has any activities running in the foreground, + * and if it does, registers a callback for when that UID no longer has any foreground + * activities. This is done atomically, so callers can be ensured that they will + * get a callback if this method returns true. + * + * @param callback Callback to be called + * @param uid The UID to be checked + * @return true when the UID has an Activity in the foreground and the callback + * , false otherwise + */ + public boolean registerUidToBackgroundCallback(Callback callback, int uid) { + synchronized (mLock) { + if (!isInForegroundLocked(uid)) { + return false; + } + // This uid is in the foreground; register callback for when it moves + // into the background. + List callbacks = mBackgroundCallbacks.get(uid, new ArrayList()); + callbacks.add(callback); + mBackgroundCallbacks.put(uid, callbacks); + return true; + } + } + + /** + * @param uid The UID to be checked + * @return whether the UID has any activities running in the foreground + */ + public boolean isInForeground(int uid) { + synchronized (mLock) { + return isInForegroundLocked(uid); + } + } + + /** + * @return a list of UIDs currently in the foreground, or an empty list + * if none are found. + */ + public List getForegroundUids() { + ArrayList uids = new ArrayList(mForegroundUidPids.size()); + synchronized (mLock) { + for (int i = 0; i < mForegroundUidPids.size(); i++) { + uids.add(mForegroundUidPids.keyAt(i)); + } + } + return uids; + } + + private boolean isInForegroundLocked(int uid) { + return mForegroundUidPids.get(uid) != null; + } + + private void handleUidToBackground(int uid) { + ArrayList pendingCallbacks = null; + synchronized (mLock) { + List callbacks = mBackgroundCallbacks.get(uid); + if (callbacks != null) { + pendingCallbacks = new ArrayList(callbacks); + // Only call them once + mBackgroundCallbacks.remove(uid); + } + } + // Release lock for callbacks + if (pendingCallbacks != null) { + for (Callback callback : pendingCallbacks) { + callback.onUidToBackground(uid); + } + } + } + + @Override + public void onForegroundActivitiesChanged(int pid, int uid, + boolean hasForegroundActivities) throws RemoteException { + boolean uidToBackground = false; + synchronized (mLock) { + SparseBooleanArray foregroundPids = mForegroundUidPids.get(uid, + new SparseBooleanArray()); + if (hasForegroundActivities) { + foregroundPids.put(pid, true); + } else { + foregroundPids.delete(pid); + } + if (foregroundPids.size() == 0) { + mForegroundUidPids.remove(uid); + uidToBackground = true; + } else { + mForegroundUidPids.put(uid, foregroundPids); + } + } + if (uidToBackground) { + handleUidToBackground(uid); + } + if (DBG) { + if (DBG) Log.d(TAG, "Foreground changed, PID: " + Integer.toString(pid) + " UID: " + + Integer.toString(uid) + " foreground: " + + hasForegroundActivities); + synchronized (mLock) { + Log.d(TAG, "Foreground UID/PID combinations:"); + for (int i = 0; i < mForegroundUidPids.size(); i++) { + int foregroundUid = mForegroundUidPids.keyAt(i); + SparseBooleanArray foregroundPids = mForegroundUidPids.get(foregroundUid); + if (foregroundPids.size() == 0) { + Log.e(TAG, "No PIDS associated with foreground UID!"); + } + for (int j = 0; j < foregroundPids.size(); j++) + Log.d(TAG, "UID: " + Integer.toString(foregroundUid) + " PID: " + + Integer.toString(foregroundPids.keyAt(j))); + } + } + } + } + + + @Override + public void onProcessDied(int pid, int uid) throws RemoteException { + if (DBG) Log.d(TAG, "Process died; UID " + Integer.toString(uid) + " PID " + + Integer.toString(pid)); + onForegroundActivitiesChanged(pid, uid, false); + } + + @Override + public void onImportanceChanged(int pid, int uid, int importance) + throws RemoteException { + // Don't care + } +} diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java index 4d81ae71..36f8ff50 100755 --- a/src/com/android/nfc/NfcService.java +++ b/src/com/android/nfc/NfcService.java @@ -278,6 +278,7 @@ public class NfcService implements DeviceHostListener { private HostEmulationManager mHostEmulationManager; private AidRoutingManager mAidRoutingManager; + private ForegroundUtils mForegroundUtils; private static NfcService sService; public static void enforceAdminPerm(Context context) { @@ -530,6 +531,8 @@ public class NfcService implements DeviceHostListener { updatePackageCache(); } + mForegroundUtils = ForegroundUtils.getInstance(); + new EnableDisableTask().execute(TASK_BOOT); // do blocking boot tasks } @@ -1011,6 +1014,10 @@ public class NfcService implements DeviceHostListener { IntentFilter[] filters, TechListParcel techListsParcel) { mContext.enforceCallingOrSelfPermission(NFC_PERM, NFC_PERM_ERROR); + if (!mForegroundUtils.isInForeground(Binder.getCallingUid())) { + Log.e(TAG, "setForegroundDispatch: Caller not in foreground."); + return; + } // Short-cut the disable path if (intent == null && filters == null && techListsParcel == null) { mNfcDispatcher.setForegroundDispatch(null, null, null); @@ -1092,6 +1099,10 @@ public class NfcService implements DeviceHostListener { @Override public void setReaderMode(IBinder binder, IAppCallback callback, int flags, Bundle extras) throws RemoteException { + if (!mForegroundUtils.isInForeground(Binder.getCallingUid())) { + Log.e(TAG, "setReaderMode: Caller not in foreground."); + return; + } synchronized (NfcService.this) { if (flags != 0) { try { -- cgit v1.2.3