Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8e3d7577 authored by Kevin Chyn's avatar Kevin Chyn
Browse files

3/n: Move internal cleanup to its own ClientMonitor subclass

InternalRemovalClient and InternalEnumerateClient are only used within
InternalCleanupClient, which keeps track of the cleanup state. This
allows us to remove a lot of unnecessary global state.

Bug: 157790417
Test: Manually cause mismatched templates by modifying FingerprintUtils.
      Mismatched Framework/HAL templates are properly cleaned up.

Change-Id: I100e579f5343114644889e3527923e56bdd1972f
parent 7599d256
Loading
Loading
Loading
Loading
+18 −184
Original line number Diff line number Diff line
@@ -79,7 +79,6 @@ public abstract class BiometricServiceBase extends SystemService
    protected static final boolean DEBUG = true;

    private static final boolean CLEANUP_UNKNOWN_TEMPLATES = true;
    private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user";
    private static final int MSG_USER_SWITCHING = 10;
    private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms

@@ -114,9 +113,6 @@ public abstract class BiometricServiceBase extends SystemService
        }
    };

    private final IBinder mToken = new Binder(); // Used for internal enumeration
    private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();

    private IBiometricService mBiometricService;
    private ClientMonitor mCurrentClient;
    private ClientMonitor mPendingClient;
@@ -255,87 +251,6 @@ public abstract class BiometricServiceBase extends SystemService
        }
    }

    /**
     * Internal class to help clean up unknown templates in the HAL and Framework
     */
    private final class InternalEnumerateClient extends EnumerateClient {

        private BiometricUtils mUtils;
        // List of templates that are known to the Framework. Remove from this list when enumerate
        // returns a template that contains a match.
        private List<? extends BiometricAuthenticator.Identifier> mEnrolledList;
        // List of templates to remove from the HAL
        private List<BiometricAuthenticator.Identifier> mUnknownHALTemplates = new ArrayList<>();

        InternalEnumerateClient(Context context,
                DaemonWrapper daemon, IBinder token,
                ClientMonitorCallbackConverter listener, int groupId, int userId,
                boolean restricted, String owner,
                List<? extends BiometricAuthenticator.Identifier> enrolledList,
                BiometricUtils utils, int sensorId) {
            super(context, getConstants(), daemon, token, listener, groupId, userId,
                    restricted, owner, sensorId, statsModality());
            mEnrolledList = enrolledList;
            mUtils = utils;
        }

        private void handleEnumeratedTemplate(BiometricAuthenticator.Identifier identifier) {
            if (identifier == null) {
                return;
            }
            Slog.v(getTag(), "handleEnumeratedTemplate: " + identifier.getBiometricId());
            boolean matched = false;
            for (int i = 0; i < mEnrolledList.size(); i++) {
                if (mEnrolledList.get(i).getBiometricId() == identifier.getBiometricId()) {
                    mEnrolledList.remove(i);
                    matched = true;
                    break;
                }
            }

            // TemplateId 0 means no templates in HAL
            if (!matched && identifier.getBiometricId() != 0) {
                mUnknownHALTemplates.add(identifier);
            }
            Slog.v(getTag(), "Matched: " + matched);
        }

        private void doTemplateCleanup() {
            if (mEnrolledList == null) {
                return;
            }

            // At this point, mEnrolledList only contains templates known to the framework and
            // not the HAL.
            for (int i = 0; i < mEnrolledList.size(); i++) {
                BiometricAuthenticator.Identifier identifier = mEnrolledList.get(i);
                Slog.e(getTag(), "doTemplateCleanup(): Removing dangling template from framework: "
                        + identifier.getBiometricId() + " "
                        + identifier.getName());
                mUtils.removeBiometricForUser(getContext(),
                        getTargetUserId(), identifier.getBiometricId());
                FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
                        statsModality(),
                        BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK);
            }
            mEnrolledList.clear();
        }

        public List<BiometricAuthenticator.Identifier> getUnknownHALTemplates() {
            return mUnknownHALTemplates;
        }

        @Override
        public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
                int remaining) {
            handleEnumeratedTemplate(identifier);
            if (remaining == 0) {
                doTemplateCleanup();
            }
            return remaining == 0;
        }
    }

    /**
     * Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor
     * subclasses.
@@ -474,19 +389,6 @@ public abstract class BiometricServiceBase extends SystemService
        }
    }

    /**
     * Container for enumerated templates. Used to keep track when cleaning up unknown
     * templates.
     */
    private final class UserTemplate {
        final BiometricAuthenticator.Identifier mIdentifier;
        final int mUserId;
        UserTemplate(BiometricAuthenticator.Identifier identifier, int userId) {
            this.mIdentifier = identifier;
            this.mUserId = userId;
        }
    }

    /**
     * Initializes the system service.
     * <p>
@@ -615,11 +517,6 @@ public abstract class BiometricServiceBase extends SystemService
        if (DEBUG) Slog.v(getTag(), "handleError(client="
                + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");

        if (client instanceof InternalRemovalClient
                || client instanceof InternalEnumerateClient) {
            clearEnumerateState();
        }

        if (client != null && client.onError(error, vendorCode)) {
            removeClient(client);
        }
@@ -653,39 +550,12 @@ public abstract class BiometricServiceBase extends SystemService
                updateActiveGroup(userId, null);
            }
        }

        if (client instanceof InternalRemovalClient && !mUnknownHALTemplates.isEmpty()) {
            startCleanupUnknownHALTemplates();
        } else if (client instanceof InternalRemovalClient) {
            clearEnumerateState();
        }
    }

    protected void handleEnumerate(BiometricAuthenticator.Identifier identifier, int remaining) {
        ClientMonitor client = mCurrentClient;
        if (client != null) {
            client.onEnumerationResult(identifier, remaining);
        }

        // All templates in the HAL for this user were enumerated
        if (remaining == 0) {
            if (client instanceof InternalEnumerateClient) {
                List<BiometricAuthenticator.Identifier> unknownHALTemplates =
                        ((InternalEnumerateClient) client).getUnknownHALTemplates();

                if (!unknownHALTemplates.isEmpty()) {
                    Slog.w(getTag(), "Adding " + unknownHALTemplates.size()
                            + " templates for deletion");
                }
                for (int i = 0; i < unknownHALTemplates.size(); i++) {
                    mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplates.get(i),
                            client.getTargetUserId()));
                }
        if (client != null && client.onEnumerationResult(identifier, remaining)) {
            removeClient(client);
                startCleanupUnknownHALTemplates();
            } else if (client != null) {
                removeClient(client);
            }
        }
    }

@@ -812,7 +682,7 @@ public abstract class BiometricServiceBase extends SystemService
        });
    }

    protected void enumerateInternal(EnumerateClient client) {
    protected void cleanupInternal(InternalCleanupClient client) {
        mHandler.post(() -> {
            startClient(client, true /* initiatedByClient */);
        });
@@ -940,8 +810,7 @@ public abstract class BiometricServiceBase extends SystemService
                    currentClient.getOwnerString());
            // This check only matters for FingerprintService, since enumerate may call back
            // multiple times.
            if (currentClient instanceof InternalEnumerateClient
                    || currentClient instanceof InternalRemovalClient) {
            if (currentClient instanceof InternalCleanupClient) {
                // This condition means we're currently running internal diagnostics to
                // remove extra templates in the hardware and/or the software
                // TODO: design an escape hatch in case client never finishes
@@ -997,7 +866,7 @@ public abstract class BiometricServiceBase extends SystemService
        }

        if (DEBUG) Slog.v(getTag(), "starting client "
                + mCurrentClient.getClass().getSuperclass().getSimpleName()
                + mCurrentClient.getClass().getSimpleName()
                + "(" + mCurrentClient.getOwnerString() + ")"
                + " targetUserId: " + mCurrentClient.getTargetUserId()
                + " currentUserId: " + mCurrentUserId
@@ -1026,7 +895,9 @@ public abstract class BiometricServiceBase extends SystemService
            }
        }
        if (mCurrentClient != null) {
            if (DEBUG) Slog.v(getTag(), "Done with client: " + mCurrentClient.getOwnerString());
            if (DEBUG) Slog.v(getTag(), "Done with client: "
                    + mCurrentClient.getClass().getSimpleName()
                    + "(" + mCurrentClient.getOwnerString() + ")");
            mCurrentClient = null;
        }
        if (mPendingClient == null) {
@@ -1118,54 +989,18 @@ public abstract class BiometricServiceBase extends SystemService
     */
    protected void doTemplateCleanupForUser(int userId) {
        if (CLEANUP_UNKNOWN_TEMPLATES) {
            enumerateUser(userId);
        }
    }

    private void clearEnumerateState() {
        if (DEBUG) Slog.v(getTag(), "clearEnumerateState()");
        mUnknownHALTemplates.clear();
    }

    /**
     * Remove unknown templates from HAL
     */
    private void startCleanupUnknownHALTemplates() {
        if (!mUnknownHALTemplates.isEmpty()) {
            UserTemplate template = mUnknownHALTemplates.get(0);
            mUnknownHALTemplates.remove(template);
            boolean restricted = !hasPermission(getManageBiometricPermission());
            InternalRemovalClient client = new InternalRemovalClient(getContext(),
                    getConstants(), getDaemonWrapper(), mToken, null /* listener */,
                    template.mIdentifier.getBiometricId(), 0 /* groupId */, template.mUserId,
                    restricted, getContext().getPackageName(), getBiometricUtils(),
                    getSensorId(), statsModality());
            removeInternal(client);
            FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
                    statsModality(),
                    BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
        } else {
            clearEnumerateState();
            if (mPendingClient != null) {
                Slog.d(getTag(), "Enumerate finished, starting pending client");
                startClient(mPendingClient, false /* initiatedByClient */);
                mPendingClient = null;
            }
        }
    }

    private void enumerateUser(int userId) {
        if (DEBUG) Slog.v(getTag(), "Enumerating user(" + userId + ")");
            if (DEBUG) Slog.v(getTag(), "Cleaning up templates for user(" + userId + ")");

            final boolean restricted = !hasPermission(getManageBiometricPermission());
            final List<? extends BiometricAuthenticator.Identifier> enrolledList =
                    getEnrolledTemplates(userId);

        InternalEnumerateClient client = new InternalEnumerateClient(getContext(),
                getDaemonWrapper(), mToken, null /* serviceListener */, userId,
                userId, restricted, getContext().getOpPackageName(), enrolledList,
                getBiometricUtils(), getSensorId());
        enumerateInternal(client);
            InternalCleanupClient client = new InternalCleanupClient(getContext(), getConstants(),
                    getDaemonWrapper(), null /* serviceListener */, userId, userId,
                    restricted, getContext().getOpPackageName(), getSensorId(), statsModality(),
                    enrolledList, getBiometricUtils());
            cleanupInternal(client);
        }
    }

    /**
@@ -1173,8 +1008,7 @@ public abstract class BiometricServiceBase extends SystemService
     * HAL.
     */
    protected void handleUserSwitching(int userId) {
        if (getCurrentClient() instanceof InternalRemovalClient
                || getCurrentClient() instanceof InternalEnumerateClient) {
        if (getCurrentClient() instanceof InternalCleanupClient) {
            Slog.w(getTag(), "User switched while performing cleanup");
        }
        updateActiveGroup(userId, null);
+157 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.biometrics.sensors;

import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.util.Slog;

import com.android.internal.util.FrameworkStatsLog;

import java.util.ArrayList;
import java.util.List;

/**
 * Wraps {@link InternalEnumerateClient} and {@link RemovalClient}. Keeps track of all the
 * internal states when cleaning up mismatch between framework and HAL templates. This client
 * ends either when
 * 1) The HAL and Framework are in sync, and
 * {@link #onEnumerationResult(BiometricAuthenticator.Identifier, int)} returns true, or
 * 2) The HAL and Framework are not in sync, and
 * {@link #onRemoved(BiometricAuthenticator.Identifier, int)} returns true/
 */
public class InternalCleanupClient extends ClientMonitor {
    /**
     * Container for enumerated templates. Used to keep track when cleaning up unknown
     * templates.
     */
    private static final class UserTemplate {
        final BiometricAuthenticator.Identifier mIdentifier;
        final int mUserId;
        UserTemplate(BiometricAuthenticator.Identifier identifier, int userId) {
            this.mIdentifier = identifier;
            this.mUserId = userId;
        }
    }

    private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
    private final BiometricUtils mBiometricUtils;
    private ClientMonitor mCurrentTask;

    public InternalCleanupClient(Context context, Constants constants,
            BiometricServiceBase.DaemonWrapper daemon,
            ClientMonitorCallbackConverter listener,
            int userId, int groupId, boolean restricted, String owner, int sensorId,
            int statsModality, List<? extends BiometricAuthenticator.Identifier> enrolledList,
            BiometricUtils utils) {
        super(context, constants, daemon, null /* token */, listener, userId, groupId, restricted,
                owner, 0 /* cookie */, sensorId, statsModality,
                BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN);

        mBiometricUtils = utils;
        mCurrentTask = new InternalEnumerateClient(context, constants, daemon, getToken(),
                listener, userId, groupId, restricted, owner, enrolledList, utils, sensorId,
                statsModality);
    }

    private void startCleanupUnknownHalTemplates() {
        UserTemplate template = mUnknownHALTemplates.get(0);
        mUnknownHALTemplates.remove(template);
        mCurrentTask = new RemovalClient(getContext(),
                mConstants, getDaemonWrapper(), getToken(), null /* listener */,
                template.mIdentifier.getBiometricId(), 0 /* groupId */, template.mUserId,
                getIsRestricted(), getContext().getPackageName(), mBiometricUtils,
                getSensorId(), mStatsModality);
        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
                mStatsModality,
                BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
        mCurrentTask.start();
    }

    @Override
    public int start() {
        // Start enumeration. Removal will start if necessary, when enumeration is completed.
        return mCurrentTask.start();
    }

    @Override
    public int stop(boolean initiatedByClient) {
        return 0;
    }

    @Override
    public boolean onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
        if (!(mCurrentTask instanceof RemovalClient)) {
            Slog.e(getLogTag(), "onRemoved received during client: "
                    + mCurrentTask.getClass().getSimpleName());
            return false;
        }
        return mCurrentTask.onRemoved(identifier, remaining);
    }

    @Override
    public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
            int remaining) {
        if (!(mCurrentTask instanceof InternalEnumerateClient)) {
            Slog.e(getLogTag(), "onEnumerationResult received during client: "
                    + mCurrentTask.getClass().getSimpleName());
            return false;
        }

        mCurrentTask.onEnumerationResult(identifier, remaining);

        if (remaining != 0) {
            return false;
        }

        final List<BiometricAuthenticator.Identifier> unknownHALTemplates =
                ((InternalEnumerateClient) mCurrentTask).getUnknownHALTemplates();

        if (!unknownHALTemplates.isEmpty()) {
            Slog.w(getLogTag(), "Adding " + unknownHALTemplates.size()
                    + " templates for deletion");
        }
        for (int i = 0; i < unknownHALTemplates.size(); i++) {
            mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplates.get(i),
                    mCurrentTask.getTargetUserId()));
        }

        if (mUnknownHALTemplates.isEmpty()) {
            return true;
        } else {
            startCleanupUnknownHalTemplates();
            return false;
        }
    }

    @Override
    public boolean onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
        return false;
    }

    @Override
    public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
            boolean authenticated, ArrayList<Byte> token) {
        return false;
    }

    @Override
    public void notifyUserActivity() {

    }
}
+108 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.biometrics.sensors;

import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.util.Slog;

import com.android.internal.util.FrameworkStatsLog;

import java.util.ArrayList;
import java.util.List;

/**
 * Internal class to help clean up unknown templates in the HAL and Framework
 */
public class InternalEnumerateClient extends EnumerateClient {
    private BiometricUtils mUtils;
    // List of templates that are known to the Framework. Remove from this list when enumerate
    // returns a template that contains a match.
    private List<? extends BiometricAuthenticator.Identifier> mEnrolledList;
    // List of templates to remove from the HAL
    private List<BiometricAuthenticator.Identifier> mUnknownHALTemplates = new ArrayList<>();

    InternalEnumerateClient(Context context, Constants constants,
            BiometricServiceBase.DaemonWrapper daemon, IBinder token,
            ClientMonitorCallbackConverter listener, int groupId, int userId,
            boolean restricted, String owner,
            List<? extends BiometricAuthenticator.Identifier> enrolledList,
            BiometricUtils utils, int sensorId, int statsModality) {
        super(context, constants, daemon, token, listener, groupId, userId,
                restricted, owner, sensorId, statsModality);
        mEnrolledList = enrolledList;
        mUtils = utils;
    }

    private void handleEnumeratedTemplate(BiometricAuthenticator.Identifier identifier) {
        if (identifier == null) {
            return;
        }
        Slog.v(getLogTag(), "handleEnumeratedTemplate: " + identifier.getBiometricId());
        boolean matched = false;
        for (int i = 0; i < mEnrolledList.size(); i++) {
            if (mEnrolledList.get(i).getBiometricId() == identifier.getBiometricId()) {
                mEnrolledList.remove(i);
                matched = true;
                break;
            }
        }

        // TemplateId 0 means no templates in HAL
        if (!matched && identifier.getBiometricId() != 0) {
            mUnknownHALTemplates.add(identifier);
        }
        Slog.v(getLogTag(), "Matched: " + matched);
    }

    private void doTemplateCleanup() {
        if (mEnrolledList == null) {
            return;
        }

        // At this point, mEnrolledList only contains templates known to the framework and
        // not the HAL.
        for (int i = 0; i < mEnrolledList.size(); i++) {
            BiometricAuthenticator.Identifier identifier = mEnrolledList.get(i);
            Slog.e(getLogTag(), "doTemplateCleanup(): Removing dangling template from framework: "
                    + identifier.getBiometricId() + " "
                    + identifier.getName());
            mUtils.removeBiometricForUser(getContext(),
                    getTargetUserId(), identifier.getBiometricId());
            FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
                    mStatsModality,
                    BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK);
        }
        mEnrolledList.clear();
    }

    public List<BiometricAuthenticator.Identifier> getUnknownHALTemplates() {
        return mUnknownHALTemplates;
    }

    @Override
    public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier,
            int remaining) {
        handleEnumeratedTemplate(identifier);
        if (remaining == 0) {
            doTemplateCleanup();
        }
        return remaining == 0;
    }
}
+0 −34
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.biometrics.sensors;

import android.content.Context;
import android.os.IBinder;

/**
 * Removal client specific for internal-cleanup, e.g. mismatched HAL/Framework templates
 */
public class InternalRemovalClient extends RemovalClient {
    public InternalRemovalClient(Context context, Constants constants,
            BiometricServiceBase.DaemonWrapper daemon, IBinder token,
            ClientMonitorCallbackConverter listener, int templateId, int groupId, int userId,
            boolean restricted, String owner, BiometricUtils utils, int sensorId,
            int statsModality) {
        super(context, constants, daemon, token, listener, templateId, groupId,
                userId, restricted, owner, utils, sensorId, statsModality);
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@ public abstract class LoggableMonitor {
    public static final String TAG = "BiometricStats";
    public static final boolean DEBUG = false;

    private final int mStatsModality;
    final int mStatsModality;
    private final int mStatsAction;
    private final int mStatsClient;
    private long mFirstAcquireTimeMs;