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

Commit f8ff36c4 authored by Kevin Chyn's avatar Kevin Chyn
Browse files

finished implementation of fingerprintservice enumerate

fixes bug 35358801
Test: manual

Change-Id: I890148dd82484890d135532ac8f3344e6ea7fcd7
parent cf0341ec
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -58,7 +58,7 @@ public abstract class EnumerateClient extends ClientMonitor {
    public int stop(boolean initiatedByClient) {
        IBiometricsFingerprint daemon = getFingerprintDaemon();
        if (daemon == null) {
            Slog.w(TAG, "stopAuthentication: no fingerprint HAL!");
            Slog.w(TAG, "stopEnumeration: no fingerprint HAL!");
            return ERROR_ESRCH;
        }
        try {
@@ -102,12 +102,12 @@ public abstract class EnumerateClient extends ClientMonitor {
    @Override
    public boolean onEnrollResult(int fingerId, int groupId, int rem) {
        if (DEBUG) Slog.w(TAG, "onEnrollResult() called for enumerate!");
        return true; // Invalid for Remove
        return true; // Invalid for Enumerate.
    }

    @Override
    public boolean onRemoved(int fingerId, int groupId, int remaining) {
        if (DEBUG) Slog.w(TAG, "onRemoved() called for enumerate!");
        return true; // Invalid for Authenticate
        return true; // Invalid for Enumerate.
    }
}
+165 −33
Original line number Diff line number Diff line
@@ -85,6 +85,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.LinkedList;

/**
 * A service to manage multiple clients that want to access the fingerprint HAL API.
@@ -134,6 +135,20 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
    private ClientMonitor mPendingClient;
    private PerformanceStats mPerformanceStats;


    private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration
    private LinkedList<Integer> mEnumeratingUserIds = new LinkedList<>();
    private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw finterprints

    private class UserFingerprint {
        Fingerprint f;
        int userId;
        public UserFingerprint(Fingerprint f, int userId) {
            this.f = f;
            this.userId = userId;
        }
    }

    // Normal fingerprint authentications are tracked by mPerformanceMap.
    private HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();

@@ -256,10 +271,12 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
        // This operation can be expensive, so keep track of the elapsed time. Might need to move to
        // background if it takes too long.
        long t = System.currentTimeMillis();

        mAuthenticatorIds.clear();
        mEnumeratingUserIds.clear();
        mUnknownFingerprints.clear();
        for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) {
            int userId = getUserOrWorkProfileId(null, user.id);
            mEnumeratingUserIds.add(userId);
            if (!mAuthenticatorIds.containsKey(userId)) {
                updateActiveGroup(userId, null);
            }
@@ -269,12 +286,70 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
        if (t > 1000) {
            Slog.w(TAG, "loadAuthenticatorIds() taking too long: " + t + "ms");
        }

        if (!mEnumeratingUserIds.isEmpty()) {
            enumerateNextUser();
        }
    }

    private void enumerateNextUser() {
        int nextUser = mEnumeratingUserIds.getFirst();
        updateActiveGroup(nextUser, null);
        boolean restricted = !hasPermission(MANAGE_FINGERPRINT);

        if (DEBUG) Slog.v(TAG, "Enumerating user id " + nextUser + " of "
                + mEnumeratingUserIds.size() + " remaining users");

        startEnumerate(mToken, nextUser, null, restricted, true /* internal */);
    }

    // Remove unknown fingerprints from hardware
    private void cleanupUnknownFingerprints() {
        if (!mUnknownFingerprints.isEmpty()) {
            Slog.w(TAG, "unknown fingerprint size: " + mUnknownFingerprints.size());
            UserFingerprint uf = mUnknownFingerprints.get(0);
            mUnknownFingerprints.remove(uf);
            boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
            updateActiveGroup(uf.userId, null);
            startRemove(mToken, uf.f.getFingerId(), uf.f.getGroupId(), uf.userId, null,
                    restricted, true /* internal */);
        }
    }

    protected void handleEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
        if (DEBUG) Slog.w(TAG, "Enumerate: fid=" + fingerId + ", gid="
                + groupId + "rem=" + remaining);
        // TODO: coordinate names with framework
        if (DEBUG) Slog.w(TAG, "Enumerate: fid=" + fingerId
                + ", gid=" + groupId
                + ", dev=" + deviceId
                + ", rem=" + remaining);

        ClientMonitor client = mCurrentClient;

        if (client != null) {
            client.onEnumerationResult(fingerId, groupId, remaining);
        }

        // All fingerprints in hardware for this user were enumerated
        if (remaining == 0) {
            mEnumeratingUserIds.poll();

            if (client instanceof InternalEnumerateClient) {
                List<Fingerprint> enrolled = ((InternalEnumerateClient) client).getEnumeratedList();
                Slog.w(TAG, "Added " + enrolled.size() + " enumerated fingerprints for deletion");
                for (Fingerprint f : enrolled) {
                    mUnknownFingerprints.add(new UserFingerprint(f, client.getTargetUserId()));
                }
            }

            removeClient(client);

            if (!mEnumeratingUserIds.isEmpty()) {
                enumerateNextUser();
            } else if (client instanceof InternalEnumerateClient) {
                if (DEBUG) Slog.v(TAG, "Finished enumerating all users");
                // This will start a chain of InternalRemovalClients
                cleanupUnknownFingerprints();
            }
        }
    }

    protected void handleError(long deviceId, int error, int vendorCode) {
@@ -296,10 +371,18 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
    }

    protected void handleRemoved(long deviceId, int fingerId, int groupId, int remaining) {
        if (DEBUG) Slog.w(TAG, "Removed: fid=" + fingerId
                + ", gid=" + groupId
                + ", dev=" + deviceId
                + ", rem=" + remaining);

        ClientMonitor client = mCurrentClient;
        if (client != null && client.onRemoved(fingerId, groupId, remaining)) {
            removeClient(client);
        }
        if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) {
            cleanupUnknownFingerprints();
        }
    }

    protected void handleAuthenticated(long deviceId, int fingerId, int groupId,
@@ -426,7 +509,15 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
        ClientMonitor currentClient = mCurrentClient;
        if (currentClient != null) {
            if (DEBUG) Slog.v(TAG, "request stop current client " + currentClient.getOwnerString());
            if (currentClient instanceof InternalEnumerateClient ||
                    currentClient instanceof InternalRemovalClient) {
                // This condition means we're currently running internal diagnostics to
                // remove extra fingerprints in the hardware and/or the software
                // TODO: design an escape hatch in case client never finishes
            }
            else {
                currentClient.stop(initiatedByClient);
            }
            mPendingClient = newClient;
            mHandler.removeCallbacks(mResetClientState);
            mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
@@ -443,12 +534,30 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
    }

    void startRemove(IBinder token, int fingerId, int groupId, int userId,
            IFingerprintServiceReceiver receiver, boolean restricted) {
            IFingerprintServiceReceiver receiver, boolean restricted, boolean internal) {
        IBiometricsFingerprint daemon = getFingerprintDaemon();
        if (daemon == null) {
            Slog.w(TAG, "startRemove: no fingerprint HAL!");
            return;
        }

        if (internal) {
            Context context = getContext();
            InternalRemovalClient client = new InternalRemovalClient(context, mHalDeviceId,
                    token, receiver, fingerId, groupId, userId, restricted,
                    context.getOpPackageName()) {
                @Override
                public void notifyUserActivity() {

                }
                @Override
                public IBiometricsFingerprint getFingerprintDaemon() {
                    return FingerprintService.this.getFingerprintDaemon();
                }
            };
            startClient(client, true);
        }
        else {
            RemovalClient client = new RemovalClient(getContext(), mHalDeviceId, token,
                    receiver, fingerId, groupId, userId, restricted, token.toString()) {
                @Override
@@ -463,14 +572,34 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
            };
            startClient(client, true);
        }
    }

    void startEnumerate(IBinder token, int userId,
        IFingerprintServiceReceiver receiver, boolean restricted) {
        IFingerprintServiceReceiver receiver, boolean restricted, boolean internal) {
        IBiometricsFingerprint daemon = getFingerprintDaemon();
        if (daemon == null) {
            Slog.w(TAG, "startEnumerate: no fingerprint HAL!");
            return;
        }
        if (internal) {
            List<Fingerprint> enrolledList = getEnrolledFingerprints(userId);
            Context context = getContext();
            InternalEnumerateClient client = new InternalEnumerateClient(context, mHalDeviceId,
                    token, receiver, userId, userId, restricted, context.getOpPackageName(),
                    enrolledList) {
                @Override
                public void notifyUserActivity() {

                }

                @Override
                public IBiometricsFingerprint getFingerprintDaemon() {
                    return FingerprintService.this.getFingerprintDaemon();
                }
            };
            startClient(client, true);
        }
        else {
            EnumerateClient client = new EnumerateClient(getContext(), mHalDeviceId, token,
                    receiver, userId, userId, restricted, token.toString()) {
                @Override
@@ -485,6 +614,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
            };
            startClient(client, true);
        }
    }

    public List<Fingerprint> getEnrolledFingerprints(int userId) {
        return mFingerprintUtils.getFingerprintsForUser(mContext, userId);
@@ -970,12 +1100,14 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    startRemove(token, fingerId, groupId, userId, receiver, restricted);
                    startRemove(token, fingerId, groupId, userId, receiver,
                            restricted, false /* internal */);
                }
            });

        }

        @Override // Binder call
        public void enumerate(final IBinder token, final int userId,
            final IFingerprintServiceReceiver receiver) {
            checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
@@ -983,7 +1115,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    startEnumerate(token, userId, receiver, restricted);
                    startEnumerate(token, userId, receiver, restricted, false /* internal */);
                }
            });

+94 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.fingerprint;

import android.content.Context;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.IBinder;
import android.util.Slog;
import java.util.ArrayList;
import java.util.List;

/**
 * An internal class to help clean up unknown fingerprints in the hardware and software
 */
public abstract class InternalEnumerateClient extends EnumerateClient {

    private List<Fingerprint> mEnrolledList;
    private List<Fingerprint> mEnumeratedList = new ArrayList<>(); // list of fp to delete

    public InternalEnumerateClient(Context context, long halDeviceId, IBinder token,
            IFingerprintServiceReceiver receiver, int groupId, int userId,
            boolean restricted, String owner, List<Fingerprint> enrolledList) {

        super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
        mEnrolledList = enrolledList;
    }

    private void handleEnumeratedFingerprint(int fingerId, int groupId, int remaining) {

        boolean matched = false;
        for (int i=0; i<mEnrolledList.size(); i++) {
            if (mEnrolledList.get(i).getFingerId() == fingerId) {
                mEnrolledList.remove(i);
                matched = true;
                Slog.e(TAG, "Matched fingerprint fid=" + fingerId);
                break;
            }
        }

        // fingerId 0 means no fingerprints are in hardware
        if (!matched && fingerId != 0) {
            Fingerprint fingerprint = new Fingerprint("", groupId, fingerId, getHalDeviceId());
            mEnumeratedList.add(fingerprint);
        }
    }

    private void doFingerprintCleanup() {

        if (mEnrolledList == null) {
            return;
        }

        for (Fingerprint f : mEnrolledList) {
            Slog.e(TAG, "Internal Enumerate: Removing dangling enrolled fingerprint: "
                    + f.getName() + " " + f.getFingerId() + " " + f.getGroupId()
                    + " " + f.getDeviceId());

            FingerprintUtils.getInstance().removeFingerprintIdForUser(getContext(),
                    f.getFingerId(), getTargetUserId());
        }
        mEnrolledList.clear();
    }

    public List<Fingerprint> getEnumeratedList() {
        return mEnumeratedList;
    }

    @Override
    public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {

        handleEnumeratedFingerprint(fingerId, groupId, remaining);
        if (remaining == 0) {
            doFingerprintCleanup();
        }

        return fingerId == 0; // done when id hits 0
    }

}
+33 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.fingerprint;

import android.content.Context;
import android.os.IBinder;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import com.android.server.fingerprint.RemovalClient;

public abstract class InternalRemovalClient extends RemovalClient {

    public InternalRemovalClient(Context context, long halDeviceId, IBinder token,
            IFingerprintServiceReceiver receiver, int fingerId, int groupId, int userId,
            boolean restricted, String owner) {

        super(context, halDeviceId, token, receiver, fingerId, groupId, userId, restricted, owner);

    }
}