1/**
2 * Copyright (C) 2016 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
17package com.android.server.fingerprint;
18
19import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
20import com.android.internal.logging.MetricsLogger;
21import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
22
23import android.content.Context;
24import android.hardware.fingerprint.Fingerprint;
25import android.hardware.fingerprint.FingerprintManager;
26import android.hardware.fingerprint.IFingerprintServiceReceiver;
27import android.os.IBinder;
28import android.os.RemoteException;
29import android.util.Slog;
30
31/**
32 * A class to keep track of the authentication state for a given client.
33 */
34public abstract class AuthenticationClient extends ClientMonitor {
35    private long mOpId;
36
37    public abstract int handleFailedAttempt();
38    public abstract void resetFailedAttempts();
39
40    public static final int LOCKOUT_NONE = 0;
41    public static final int LOCKOUT_TIMED = 1;
42    public static final int LOCKOUT_PERMANENT = 2;
43
44    public AuthenticationClient(Context context, long halDeviceId, IBinder token,
45            IFingerprintServiceReceiver receiver, int targetUserId, int groupId, long opId,
46            boolean restricted, String owner) {
47        super(context, halDeviceId, token, receiver, targetUserId, groupId, restricted, owner);
48        mOpId = opId;
49    }
50
51    @Override
52    public boolean onAuthenticated(int fingerId, int groupId) {
53        boolean result = false;
54        boolean authenticated = fingerId != 0;
55
56        IFingerprintServiceReceiver receiver = getReceiver();
57        if (receiver != null) {
58            try {
59                MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_AUTH,
60                        authenticated);
61                if (!authenticated) {
62                    receiver.onAuthenticationFailed(getHalDeviceId());
63                } else {
64                    if (DEBUG) {
65                        Slog.v(TAG, "onAuthenticated(owner=" + getOwnerString()
66                                + ", id=" + fingerId + ", gp=" + groupId + ")");
67                    }
68                    Fingerprint fp = !getIsRestricted()
69                            ? new Fingerprint("" /* TODO */, groupId, fingerId, getHalDeviceId())
70                            : null;
71                    receiver.onAuthenticationSucceeded(getHalDeviceId(), fp, getTargetUserId());
72                }
73            } catch (RemoteException e) {
74                Slog.w(TAG, "Failed to notify Authenticated:", e);
75                result = true; // client failed
76            }
77        } else {
78            result = true; // client not listening
79        }
80        if (!authenticated) {
81            if (receiver != null) {
82                vibrateError();
83            }
84            // allow system-defined limit of number of attempts before giving up
85            int lockoutMode =  handleFailedAttempt();
86            if (lockoutMode != LOCKOUT_NONE) {
87                try {
88                    Slog.w(TAG, "Forcing lockout (fp driver code should do this!), mode(" +
89                            lockoutMode + ")");
90                    stop(false);
91                    int errorCode = lockoutMode == LOCKOUT_TIMED ?
92                            FingerprintManager.FINGERPRINT_ERROR_LOCKOUT :
93                            FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
94                    receiver.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
95                } catch (RemoteException e) {
96                    Slog.w(TAG, "Failed to notify lockout:", e);
97                }
98            }
99            result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
100        } else {
101            if (receiver != null) {
102                vibrateSuccess();
103            }
104            result |= true; // we have a valid fingerprint, done
105            resetFailedAttempts();
106        }
107        return result;
108    }
109
110    /**
111     * Start authentication
112     */
113    @Override
114    public int start() {
115        IBiometricsFingerprint daemon = getFingerprintDaemon();
116        if (daemon == null) {
117            Slog.w(TAG, "start authentication: no fingerprint HAL!");
118            return ERROR_ESRCH;
119        }
120        try {
121            final int result = daemon.authenticate(mOpId, getGroupId());
122            if (result != 0) {
123                Slog.w(TAG, "startAuthentication failed, result=" + result);
124                MetricsLogger.histogram(getContext(), "fingeprintd_auth_start_error", result);
125                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
126                return result;
127            }
128            if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
129        } catch (RemoteException e) {
130            Slog.e(TAG, "startAuthentication failed", e);
131            return ERROR_ESRCH;
132        }
133        return 0; // success
134    }
135
136    @Override
137    public int stop(boolean initiatedByClient) {
138        if (mAlreadyCancelled) {
139            Slog.w(TAG, "stopAuthentication: already cancelled!");
140            return 0;
141        }
142        IBiometricsFingerprint daemon = getFingerprintDaemon();
143        if (daemon == null) {
144            Slog.w(TAG, "stopAuthentication: no fingerprint HAL!");
145            return ERROR_ESRCH;
146        }
147        try {
148            final int result = daemon.cancel();
149            if (result != 0) {
150                Slog.w(TAG, "stopAuthentication failed, result=" + result);
151                return result;
152            }
153            if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is no longer authenticating");
154        } catch (RemoteException e) {
155            Slog.e(TAG, "stopAuthentication failed", e);
156            return ERROR_ESRCH;
157        }
158        mAlreadyCancelled = true;
159        return 0; // success
160    }
161
162    @Override
163    public boolean onEnrollResult(int fingerId, int groupId, int remaining) {
164        if (DEBUG) Slog.w(TAG, "onEnrollResult() called for authenticate!");
165        return true; // Invalid for Authenticate
166    }
167
168    @Override
169    public boolean onRemoved(int fingerId, int groupId, int remaining) {
170        if (DEBUG) Slog.w(TAG, "onRemoved() called for authenticate!");
171        return true; // Invalid for Authenticate
172    }
173
174    @Override
175    public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
176        if (DEBUG) Slog.w(TAG, "onEnumerationResult() called for authenticate!");
177        return true; // Invalid for Authenticate
178    }
179}
180