1/**
2 * Copyright (C) 2014 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 android.service.fingerprint;
18
19import android.app.ActivityManagerNative;
20import android.content.ComponentName;
21import android.content.ContentResolver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.ServiceConnection;
25import android.os.Binder;
26import android.os.Handler;
27import android.os.IBinder;
28import android.os.RemoteException;
29import android.os.UserHandle;
30import android.provider.Settings;
31import android.util.Log;
32import android.util.Slog;
33
34/**
35 * A class that coordinates access to the fingerprint hardware.
36 * @hide
37 */
38
39public class FingerprintManager {
40    private static final String TAG = "FingerprintManager";
41    private static final boolean DEBUG = true;
42    private static final int MSG_ENROLL_RESULT = 100;
43    private static final int MSG_ACQUIRED = 101;
44    private static final int MSG_PROCESSED = 102;
45    private static final int MSG_ERROR = 103;
46    private static final int MSG_REMOVED = 104;
47
48    // Errors generated by layers above HAL
49    public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10;
50
51    // Message types.  Must agree with HAL (fingerprint.h)
52    public static final int FINGERPRINT_ERROR = -1;
53    public static final int FINGERPRINT_ACQUIRED = 1;
54    public static final int FINGERPRINT_PROCESSED = 2;
55    public static final int FINGERPRINT_TEMPLATE_ENROLLING = 3;
56    public static final int FINGERPRINT_TEMPLATE_REMOVED = 4;
57
58    // Error messages. Must agree with HAL (fingerprint.h)
59    public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
60    public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
61    public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
62    public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
63
64    // FINGERPRINT_ACQUIRED messages.  Must agree with HAL (fingerprint.h)
65    public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
66    public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
67    public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
68    public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 4;
69    public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 8;
70    public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 16;
71
72    private IFingerprintService mService;
73    private FingerprintManagerReceiver mClientReceiver;
74    private Context mContext;
75    private IBinder mToken = new Binder();
76
77    private Handler mHandler = new Handler() {
78        public void handleMessage(android.os.Message msg) {
79            if (mClientReceiver != null) {
80                switch(msg.what) {
81                    case MSG_ENROLL_RESULT:
82                        mClientReceiver.onEnrollResult(msg.arg1, msg.arg2);
83                        break;
84                    case MSG_ACQUIRED:
85                        mClientReceiver.onAcquired(msg.arg1);
86                        break;
87                    case MSG_PROCESSED:
88                        mClientReceiver.onProcessed(msg.arg1);
89                        break;
90                    case MSG_ERROR:
91                        mClientReceiver.onError(msg.arg1);
92                        break;
93                    case MSG_REMOVED:
94                        mClientReceiver.onRemoved(msg.arg1);
95                }
96            }
97        }
98    };
99
100    /**
101     * @hide
102     */
103    public FingerprintManager(Context context, IFingerprintService service) {
104        mContext = context;
105        mService = service;
106        if (mService == null) {
107            Slog.v(TAG, "FingerprintManagerService was null");
108        }
109    }
110
111    private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
112
113        public void onEnrollResult(int fingerprintId,  int remaining) {
114            mHandler.obtainMessage(MSG_ENROLL_RESULT, fingerprintId, remaining).sendToTarget();
115        }
116
117        public void onAcquired(int acquireInfo) {
118            mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0).sendToTarget();
119        }
120
121        public void onProcessed(int fingerprintId) {
122            mHandler.obtainMessage(MSG_PROCESSED, fingerprintId, 0).sendToTarget();
123        }
124
125        public void onError(int error) {
126            mHandler.obtainMessage(MSG_ERROR, error, 0).sendToTarget();
127        }
128
129        public void onRemoved(int fingerprintId) {
130            mHandler.obtainMessage(MSG_REMOVED, fingerprintId, 0).sendToTarget();
131        }
132    };
133
134    /**
135     * Determine whether the user has at least one fingerprint enrolled and enabled.
136     *
137     * @return true if at least one is enrolled and enabled
138     */
139    public boolean enrolledAndEnabled() {
140        ContentResolver res = mContext.getContentResolver();
141        return Settings.Secure.getInt(res, "fingerprint_enabled", 0) != 0
142                && FingerprintUtils.getFingerprintIdsForUser(res, getCurrentUserId()).length > 0;
143    }
144
145    /**
146     * Start the enrollment process.  Timeout dictates how long to wait for the user to
147     * enroll a fingerprint.
148     *
149     * @param timeout
150     */
151    public void enroll(long timeout) {
152        if (mServiceReceiver == null) {
153            sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
154            return;
155        }
156        if (mService != null) try {
157            mService.enroll(mToken, timeout, getCurrentUserId());
158        } catch (RemoteException e) {
159            Log.v(TAG, "Remote exception while enrolling: ", e);
160            sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
161        }
162    }
163
164    /**
165     * Remove the given fingerprintId from the system.  FingerprintId of 0 has special meaning
166     * which is to delete all fingerprint data for the current user. Use with caution.
167     * @param fingerprintId
168     */
169    public void remove(int fingerprintId) {
170        if (mServiceReceiver == null) {
171            sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
172            return;
173        }
174        if (mService != null) {
175            try {
176                mService.remove(mToken, fingerprintId, getCurrentUserId());
177            } catch (RemoteException e) {
178                Log.v(TAG, "Remote exception during remove of fingerprintId: " + fingerprintId, e);
179            }
180        } else {
181            Log.w(TAG, "remove(): Service not connected!");
182            sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
183        }
184    }
185
186    /**
187     * Starts listening for fingerprint events.  When a finger is scanned or recognized, the
188     * client will be notified via the callback.
189     */
190    public void startListening(FingerprintManagerReceiver receiver) {
191        mClientReceiver = receiver;
192        if (mService != null) {
193            try {
194                mService.startListening(mToken, mServiceReceiver, getCurrentUserId());
195            } catch (RemoteException e) {
196                Log.v(TAG, "Remote exception in startListening(): ", e);
197            }
198        } else {
199            Log.w(TAG, "startListening(): Service not connected!");
200            sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
201        }
202    }
203
204    private int getCurrentUserId() {
205        try {
206            return ActivityManagerNative.getDefault().getCurrentUser().id;
207        } catch (RemoteException e) {
208            Log.w(TAG, "Failed to get current user id\n");
209            return UserHandle.USER_NULL;
210        }
211    }
212
213    /**
214     * Stops the client from listening to fingerprint events.
215     */
216    public void stopListening() {
217        if (mService != null) {
218            try {
219                mService.stopListening(mToken, getCurrentUserId());
220                mClientReceiver = null;
221            } catch (RemoteException e) {
222                Log.v(TAG, "Remote exception in stopListening(): ", e);
223            }
224        } else {
225            Log.w(TAG, "stopListening(): Service not connected!");
226            sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
227        }
228    }
229
230    public void enrollCancel() {
231        if (mServiceReceiver == null) {
232            sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
233            return;
234        }
235        if (mService != null) {
236            try {
237                mService.enrollCancel(mToken, getCurrentUserId());
238                mClientReceiver = null;
239            } catch (RemoteException e) {
240                Log.v(TAG, "Remote exception in enrollCancel(): ", e);
241                sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
242            }
243        } else {
244            Log.w(TAG, "enrollCancel(): Service not connected!");
245        }
246    }
247
248    private void sendError(int msg, int arg1, int arg2) {
249        mHandler.obtainMessage(msg, arg1, arg2);
250    }
251}