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.Manifest;
20import android.content.Context;
21import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
22import android.hardware.fingerprint.FingerprintManager;
23import android.hardware.fingerprint.IFingerprintServiceReceiver;
24import android.media.AudioAttributes;
25import android.os.IBinder;
26import android.os.RemoteException;
27import android.os.VibrationEffect;
28import android.os.Vibrator;
29import android.util.Slog;
30
31import java.util.NoSuchElementException;
32
33/**
34 * Abstract base class for keeping track and dispatching events from fingerprint HAL to the
35 * the current client.  Subclasses are responsible for coordinating the interaction with
36 * fingerprint HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
37 */
38public abstract class ClientMonitor implements IBinder.DeathRecipient {
39    protected static final String TAG = FingerprintService.TAG; // TODO: get specific name
40    protected static final int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. See errno.h.
41    protected static final boolean DEBUG = FingerprintService.DEBUG;
42    private static final long[] DEFAULT_SUCCESS_VIBRATION_PATTERN = new long[] {0, 30};
43    private static final AudioAttributes FINGERPRINT_SONFICATION_ATTRIBUTES =
44            new AudioAttributes.Builder()
45                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
46                    .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
47                    .build();
48    private final Context mContext;
49    private final long mHalDeviceId;
50    private final int mTargetUserId;
51    private final int mGroupId;
52    // True if client does not have MANAGE_FINGERPRINT permission
53    private final boolean mIsRestricted;
54    private final String mOwner;
55    private final VibrationEffect mSuccessVibrationEffect;
56    private final VibrationEffect mErrorVibrationEffect;
57    private IBinder mToken;
58    private IFingerprintServiceReceiver mReceiver;
59    protected boolean mAlreadyCancelled;
60
61    /**
62     * @param context context of FingerprintService
63     * @param halDeviceId the HAL device ID of the associated fingerprint hardware
64     * @param token a unique token for the client
65     * @param receiver recipient of related events (e.g. authentication)
66     * @param userId target user id for operation
67     * @param groupId groupId for the fingerprint set
68     * @param restricted whether or not client has the {@link Manifest#MANAGE_FINGERPRINT}
69     * permission
70     * @param owner name of the client that owns this
71     */
72    public ClientMonitor(Context context, long halDeviceId, IBinder token,
73            IFingerprintServiceReceiver receiver, int userId, int groupId,boolean restricted,
74            String owner) {
75        mContext = context;
76        mHalDeviceId = halDeviceId;
77        mToken = token;
78        mReceiver = receiver;
79        mTargetUserId = userId;
80        mGroupId = groupId;
81        mIsRestricted = restricted;
82        mOwner = owner;
83        mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
84        mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
85        try {
86            if (token != null) {
87                token.linkToDeath(this, 0);
88            }
89        } catch (RemoteException e) {
90            Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
91        }
92    }
93
94    /**
95     * Contacts fingerprint HAL to start the client.
96     * @return 0 on success, errno from driver on failure
97     */
98    public abstract int start();
99
100    /**
101     * Contacts fingerprint HAL to stop the client.
102     * @param initiatedByClient whether the operation is at the request of a client
103     */
104    public abstract int stop(boolean initiatedByClient);
105
106    /**
107     * Method to explicitly poke powermanager on events
108     */
109    public abstract void notifyUserActivity();
110
111    /**
112     * Gets the fingerprint daemon from the cached state in the container class.
113     */
114    public abstract IBiometricsFingerprint getFingerprintDaemon();
115
116    // Event callbacks from driver. Inappropriate calls is flagged/logged by the
117    // respective client (e.g. enrolling shouldn't get authenticate events).
118    // All of these return 'true' if the operation is completed and it's ok to move
119    // to the next client (e.g. authentication accepts or rejects a fingerprint).
120    public abstract boolean onEnrollResult(int fingerId, int groupId, int rem);
121    public abstract boolean onAuthenticated(int fingerId, int groupId);
122    public abstract boolean onRemoved(int fingerId, int groupId, int remaining);
123    public abstract boolean onEnumerationResult(int fingerId, int groupId, int remaining);
124
125    /**
126     * Called when we get notification from fingerprint HAL that an image has been acquired.
127     * Common to authenticate and enroll.
128     * @param acquiredInfo info about the current image acquisition
129     * @return true if client should be removed
130     */
131    public boolean onAcquired(int acquiredInfo, int vendorCode) {
132        if (mReceiver == null)
133            return true; // client not connected
134        try {
135            mReceiver.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode);
136            return false; // acquisition continues...
137        } catch (RemoteException e) {
138            Slog.w(TAG, "Failed to invoke sendAcquired:", e);
139            return true; // client failed
140        } finally {
141            // Good scans will keep the device awake
142            if (acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
143                notifyUserActivity();
144            }
145        }
146    }
147
148    /**
149     * Called when we get notification from fingerprint HAL that an error has occurred with the
150     * current operation. Common to authenticate, enroll, enumerate and remove.
151     * @param error
152     * @return true if client should be removed
153     */
154    public boolean onError(int error, int vendorCode) {
155        if (mReceiver != null) {
156            try {
157                mReceiver.onError(getHalDeviceId(), error, vendorCode);
158            } catch (RemoteException e) {
159                Slog.w(TAG, "Failed to invoke sendError:", e);
160            }
161        }
162        return true; // errors always remove current client
163    }
164
165    public void destroy() {
166        if (mToken != null) {
167            try {
168                mToken.unlinkToDeath(this, 0);
169            } catch (NoSuchElementException e) {
170                // TODO: remove when duplicate call bug is found
171                Slog.e(TAG, "destroy(): " + this + ":", new Exception("here"));
172            }
173            mToken = null;
174        }
175        mReceiver = null;
176    }
177
178    @Override
179    public void binderDied() {
180        mToken = null;
181        mReceiver = null;
182        onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
183    }
184
185    @Override
186    protected void finalize() throws Throwable {
187        try {
188            if (mToken != null) {
189                if (DEBUG) Slog.w(TAG, "removing leaked reference: " + mToken);
190                onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
191            }
192        } finally {
193            super.finalize();
194        }
195    }
196
197    public final Context getContext() {
198        return mContext;
199    }
200
201    public final long getHalDeviceId() {
202        return mHalDeviceId;
203    }
204
205    public final String getOwnerString() {
206        return mOwner;
207    }
208
209    public final IFingerprintServiceReceiver getReceiver() {
210        return mReceiver;
211    }
212
213    public final boolean getIsRestricted() {
214        return mIsRestricted;
215    }
216
217    public final int getTargetUserId() {
218        return mTargetUserId;
219    }
220
221    public final int getGroupId() {
222        return mGroupId;
223    }
224
225    public final IBinder getToken() {
226        return mToken;
227    }
228
229    public final void vibrateSuccess() {
230        Vibrator vibrator = mContext.getSystemService(Vibrator.class);
231        if (vibrator != null) {
232            vibrator.vibrate(mSuccessVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
233        }
234    }
235
236    public final void vibrateError() {
237        Vibrator vibrator = mContext.getSystemService(Vibrator.class);
238        if (vibrator != null) {
239            vibrator.vibrate(mErrorVibrationEffect, FINGERPRINT_SONFICATION_ATTRIBUTES);
240        }
241    }
242}
243