Check fingerprint client against top activity in auth callback

Due to a race condition with activity task stack broadcasts, it's
currently possible for fingerprint authentication to succeed for a
non-top activity. This means, for example, that a malicious overlay
could be drawn in order to mislead the user about what they are
authenticating for.

This commit addresses the issue by adding a check to the
onAuthenticated() callback of the fingerprint HAL result controller to
ensure that the authenticating activity is on top at the time of
authentication. Otherwise, the pending authentication will be canceled.

Test: Follow steps from b/159249069:
1. Install com.pro100svitlo.fingerprintauthdemo from the Play store.
2. Install the PoC attack app from b/159249069.
3. Start the PoC attack app and press the "Launch PoC attack" button.
4. Use fingerprint to authenticate while the overlay is showing.

Before: Authentication succeeds, and a new activity is launched.
After: Authentication is canceled, and no new activity is launched.

Bug: 159249069
Change-Id: I289d67e5c7055ed60f7a96725c523d07cd047b23
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 4be596d..ff410fc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -18,19 +18,26 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
 import android.app.TaskStackListener;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.security.KeyStore;
+import android.util.EventLog;
 import android.util.Slog;
 
+import com.android.server.biometrics.Utils;
+
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * A class to keep track of the authentication state for a given client.
@@ -136,7 +143,54 @@
                 pm.incrementAuthForUser(getTargetUserId(), authenticated);
             }
 
+            // Ensure authentication only succeeds if the client activity is on top or is keyguard.
+            boolean isBackgroundAuth = false;
+            if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString())) {
+                try {
+                    final List<ActivityManager.RunningTaskInfo> tasks =
+                            mActivityTaskManager.getTasks(1);
+                    if (tasks == null || tasks.isEmpty()) {
+                        Slog.e(TAG, "No running tasks reported");
+                        isBackgroundAuth = true;
+                    } else {
+                        final ComponentName topActivity = tasks.get(0).topActivity;
+                        if (topActivity == null) {
+                            Slog.e(TAG, "Unable to get top activity");
+                            isBackgroundAuth = true;
+                        } else {
+                            final String topPackage = topActivity.getPackageName();
+                            if (!topPackage.contentEquals(getOwnerString())) {
+                                Slog.e(TAG, "Background authentication detected, top: " + topPackage
+                                        + ", client: " + this);
+                                isBackgroundAuth = true;
+                            }
+                        }
+                    }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Unable to get running tasks", e);
+                    isBackgroundAuth = true;
+                }
+            }
+
+            // Fail authentication if we can't confirm the client activity is on top.
+            if (isBackgroundAuth) {
+                Slog.e(TAG, "Failing possible background authentication");
+                authenticated = false;
+
+                // SafetyNet logging for exploitation attempts of b/159249069.
+                final ApplicationInfo appInfo = getContext().getApplicationInfo();
+                EventLog.writeEvent(0x534e4554, "159249069", appInfo != null ? appInfo.uid : -1,
+                        "Attempted background authentication");
+            }
+
             if (authenticated) {
+                // SafetyNet logging for b/159249069 if constraint is violated.
+                if (isBackgroundAuth) {
+                    final ApplicationInfo appInfo = getContext().getApplicationInfo();
+                    EventLog.writeEvent(0x534e4554, "159249069", appInfo != null ? appInfo.uid : -1,
+                            "Successful background authentication!");
+                }
+
                 mAlreadyDone = true;
 
                 if (listener != null) {