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 com.android.server.fingerprint;
18
19import android.app.Service;
20import android.content.ContentResolver;
21import android.content.Context;
22import android.content.Intent;
23import android.os.Handler;
24import android.os.IBinder;
25import android.os.PowerManager;
26import android.os.RemoteException;
27import android.provider.Settings;
28import android.service.fingerprint.FingerprintManager;
29import android.util.ArrayMap;
30import android.util.Slog;
31
32import com.android.server.SystemService;
33
34import android.service.fingerprint.FingerprintUtils;
35import android.service.fingerprint.IFingerprintService;
36import android.service.fingerprint.IFingerprintServiceReceiver;
37
38import java.io.PrintWriter;
39import java.lang.ref.WeakReference;
40import java.util.HashMap;
41import java.util.Map.Entry;
42import java.util.Set;
43
44/**
45 * A service to manage multiple clients that want to access the fingerprint HAL API.
46 * The service is responsible for maintaining a list of clients and dispatching all
47 * fingerprint -related events.
48 *
49 * @hide
50 */
51public class FingerprintService extends SystemService {
52    private final String TAG = "FingerprintService";
53    private static final boolean DEBUG = true;
54    private ArrayMap<IBinder, ClientData> mClients = new ArrayMap<IBinder, ClientData>();
55
56    private static final int MSG_NOTIFY = 10;
57
58    Handler mHandler = new Handler() {
59        public void handleMessage(android.os.Message msg) {
60            switch (msg.what) {
61                case MSG_NOTIFY:
62                    handleNotify(msg.arg1, msg.arg2, (Integer) msg.obj);
63                    break;
64
65                default:
66                    Slog.w(TAG, "Unknown message:" + msg.what);
67            }
68        }
69    };
70    private Context mContext;
71
72    private static final int STATE_IDLE = 0;
73    private static final int STATE_LISTENING = 1;
74    private static final int STATE_ENROLLING = 2;
75    private static final int STATE_REMOVING = 3;
76    private static final long MS_PER_SEC = 1000;
77    public static final String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
78    public static final String ENROLL_FINGERPRINT = "android.permission.ENROLL_FINGERPRINT";
79
80    private static final class ClientData {
81        public IFingerprintServiceReceiver receiver;
82        int state;
83        int userId;
84        public TokenWatcher tokenWatcher;
85        IBinder getToken() { return tokenWatcher.getToken(); }
86    }
87
88    private class TokenWatcher implements IBinder.DeathRecipient {
89        WeakReference<IBinder> token;
90
91        TokenWatcher(IBinder token) {
92            this.token = new WeakReference<IBinder>(token);
93        }
94
95        IBinder getToken() { return token.get(); }
96        public void binderDied() {
97            mClients.remove(token);
98            this.token = null;
99        }
100
101        protected void finalize() throws Throwable {
102            try {
103                if (token != null) {
104                    if (DEBUG) Slog.w(TAG, "removing leaked reference: " + token);
105                    mClients.remove(token);
106                }
107            } finally {
108                super.finalize();
109            }
110        }
111    }
112
113    public FingerprintService(Context context) {
114        super(context);
115        mContext = context;
116        nativeInit(this);
117    }
118
119    // TODO: Move these into separate process
120    // JNI methods to communicate from FingerprintManagerService to HAL
121    native int nativeEnroll(int timeout);
122    native int nativeEnrollCancel();
123    native int nativeRemove(int fingerprintId);
124    native int nativeOpenHal();
125    native int nativeCloseHal();
126    native void nativeInit(FingerprintService service);
127
128    // JNI methods for communicating from HAL to clients
129    void notify(int msg, int arg1, int arg2) {
130        mHandler.obtainMessage(MSG_NOTIFY, msg, arg1, arg2).sendToTarget();
131    }
132
133    void handleNotify(int msg, int arg1, int arg2) {
134        Slog.v(TAG, "handleNotify(msg=" + msg + ", arg1=" + arg1 + ", arg2=" + arg2 + ")");
135        for (int i = 0; i < mClients.size(); i++) {
136            ClientData clientData = mClients.valueAt(i);
137            if (clientData == null || clientData.receiver == null) {
138                if (DEBUG) Slog.v(TAG, "clientData at " + i + " is invalid!!");
139                continue;
140            }
141            switch (msg) {
142                case FingerprintManager.FINGERPRINT_ERROR: {
143                    final int error = arg1;
144                    try {
145                        clientData.receiver.onError(error);
146                    } catch (RemoteException e) {
147                        Slog.e(TAG, "can't send message to client. Did it die?", e);
148                        mClients.remove(mClients.keyAt(i));
149                    }
150                }
151                break;
152                case FingerprintManager.FINGERPRINT_ACQUIRED: {
153                    final int acquireInfo = arg1;
154                    try {
155                        clientData.receiver.onAcquired(acquireInfo);
156                    } catch (RemoteException e) {
157                        Slog.e(TAG, "can't send message to client. Did it die?", e);
158                        mClients.remove(mClients.keyAt(i));
159                    }
160                    break;
161                }
162                case FingerprintManager.FINGERPRINT_PROCESSED: {
163                    final int fingerId = arg1;
164                    try {
165                        clientData.receiver.onProcessed(fingerId);
166                    } catch (RemoteException e) {
167                        Slog.e(TAG, "can't send message to client. Did it die?", e);
168                        mClients.remove(mClients.keyAt(i));
169                    }
170                    break;
171                }
172                case FingerprintManager.FINGERPRINT_TEMPLATE_ENROLLING: {
173                    final int fingerId = arg1;
174                    final int remaining = arg2;
175                    if (clientData.state == STATE_ENROLLING) {
176                        // Only send enroll updates to clients that are actually enrolling
177                        try {
178                            clientData.receiver.onEnrollResult(fingerId, remaining);
179                        } catch (RemoteException e) {
180                            Slog.e(TAG, "can't send message to client. Did it die?", e);
181                            mClients.remove(mClients.keyAt(i));
182                        }
183                        // Update the database with new finger id.
184                        // TODO: move to client code (Settings)
185                        if (remaining == 0) {
186                            FingerprintUtils.addFingerprintIdForUser(fingerId,
187                                    mContext.getContentResolver(), clientData.userId);
188                            clientData.state = STATE_IDLE; // Nothing left to do
189                        }
190                    } else {
191                        if (DEBUG) Slog.w(TAG, "Client not enrolling");
192                        break;
193                    }
194                    break;
195                }
196                case FingerprintManager.FINGERPRINT_TEMPLATE_REMOVED: {
197                    int fingerId = arg1;
198                    if (fingerId == 0) throw new IllegalStateException("Got illegal id from HAL");
199                    FingerprintUtils.removeFingerprintIdForUser(fingerId,
200                            mContext.getContentResolver(), clientData.userId);
201                    if (clientData.receiver != null) {
202                        try {
203                            clientData.receiver.onRemoved(fingerId);
204                        } catch (RemoteException e) {
205                            Slog.e(TAG, "can't send message to client. Did it die?", e);
206                            mClients.remove(mClients.keyAt(i));
207                        }
208                    }
209                    clientData.state = STATE_LISTENING;
210                }
211                break;
212            }
213        }
214    }
215
216    void startEnroll(IBinder token, long timeout, int userId) {
217        ClientData clientData = mClients.get(token);
218        if (clientData != null) {
219            if (clientData.userId != userId) throw new IllegalStateException("Bad user");
220            clientData.state = STATE_ENROLLING;
221            nativeEnroll((int) (timeout / MS_PER_SEC));
222        } else {
223            Slog.w(TAG, "enroll(): No listener registered");
224        }
225    }
226
227    void startEnrollCancel(IBinder token, int userId) {
228        ClientData clientData = mClients.get(token);
229        if (clientData != null) {
230            if (clientData.userId != userId) throw new IllegalStateException("Bad user");
231            clientData.state = STATE_LISTENING;
232            nativeEnrollCancel();
233        } else {
234            Slog.w(TAG, "enrollCancel(): No listener registered");
235        }
236    }
237
238    // Remove all fingerprints for the given user.
239    void startRemove(IBinder token, int fingerId, int userId) {
240        ClientData clientData = mClients.get(token);
241        if (clientData != null) {
242            if (clientData.userId != userId) throw new IllegalStateException("Bad user");
243            clientData.state = STATE_REMOVING;
244            // The fingerprint id will be removed when we get confirmation from the HAL
245            int result = nativeRemove(fingerId);
246            if (result != 0) {
247                Slog.w(TAG, "Error removing fingerprint with id = " + fingerId);
248            }
249        } else {
250            Slog.w(TAG, "remove(" + token + "): No listener registered");
251        }
252    }
253
254    void addListener(IBinder token, IFingerprintServiceReceiver receiver, int userId) {
255        if (DEBUG) Slog.v(TAG, "startListening(" + receiver + ")");
256        if (mClients.get(token) == null) {
257            ClientData clientData = new ClientData();
258            clientData.state = STATE_LISTENING;
259            clientData.receiver = receiver;
260            clientData.userId = userId;
261            clientData.tokenWatcher = new TokenWatcher(token);
262            try {
263                token.linkToDeath(clientData.tokenWatcher, 0);
264                mClients.put(token, clientData);
265            } catch (RemoteException e) {
266                Slog.w(TAG, "caught remote exception in linkToDeath: ", e);
267            }
268        } else {
269            if (DEBUG) Slog.v(TAG, "listener already registered for " + token);
270        }
271    }
272
273    void removeListener(IBinder token, int userId) {
274        if (DEBUG) Slog.v(TAG, "stopListening(" + token + ")");
275        ClientData clientData = mClients.get(token);
276        if (clientData != null) {
277            token.unlinkToDeath(clientData.tokenWatcher, 0);
278            mClients.remove(token);
279        } else {
280            if (DEBUG) Slog.v(TAG, "listener not registered: " + token);
281        }
282        mClients.remove(token);
283    }
284
285    void checkPermission(String permisison) {
286        // TODO
287    }
288
289    private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
290        @Override // Binder call
291        public void enroll(IBinder token, long timeout, int userId) {
292            checkPermission(ENROLL_FINGERPRINT);
293            startEnroll(token, timeout, userId);
294        }
295
296        @Override // Binder call
297        public void enrollCancel(IBinder token,int userId) {
298            checkPermission(ENROLL_FINGERPRINT);
299            startEnrollCancel(token, userId);
300        }
301
302        @Override // Binder call
303        public void remove(IBinder token, int fingerprintId, int userId) {
304            checkPermission(ENROLL_FINGERPRINT); // TODO: Maybe have another permission
305            startRemove(token, fingerprintId, userId);
306        }
307
308        @Override // Binder call
309        public void startListening(IBinder token, IFingerprintServiceReceiver receiver, int userId)
310        {
311            checkPermission(USE_FINGERPRINT);
312            addListener(token, receiver, userId);
313        }
314
315        @Override // Binder call
316        public void stopListening(IBinder token, int userId) {
317            checkPermission(USE_FINGERPRINT);
318            removeListener(token, userId);
319        }
320    }
321
322    @Override
323    public void onStart() {
324       publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
325       nativeOpenHal();
326    }
327
328}
329