1/*
2 * Copyright (C) 2017 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.accessibility;
18
19import android.accessibilityservice.FingerprintGestureController;
20import android.hardware.fingerprint.IFingerprintClientActiveCallback;
21import android.hardware.fingerprint.IFingerprintService;
22import android.os.Binder;
23import android.os.Handler;
24import android.os.Message;
25import android.os.RemoteException;
26import android.util.Slog;
27import android.view.KeyEvent;
28
29import java.util.ArrayList;
30import java.util.List;
31
32/**
33 * Encapsulate fingerprint gesture logic
34 */
35public class FingerprintGestureDispatcher extends IFingerprintClientActiveCallback.Stub
36        implements Handler.Callback{
37    private static final int MSG_REGISTER = 1;
38    private static final int MSG_UNREGISTER = 2;
39    private static final String LOG_TAG = "FingerprintGestureDispatcher";
40
41    private final List<FingerprintGestureClient> mCapturingClients = new ArrayList<>(0);
42    private final Object mLock;
43    private final IFingerprintService mFingerprintService;
44    private final Handler mHandler;
45
46    // This field is ground truth for whether or not we are registered. Only write to it in handler.
47    private boolean mRegisteredReadOnlyExceptInHandler;
48
49    /**
50     * @param fingerprintService The system's fingerprint service
51     * @param lock A lock to use when managing internal state
52     */
53    public FingerprintGestureDispatcher(IFingerprintService fingerprintService, Object lock) {
54        mFingerprintService = fingerprintService;
55        mLock = lock;
56        mHandler = new Handler(this);
57    }
58
59    /**
60     * @param fingerprintService The system's fingerprint service
61     * @param lock A lock to use when managing internal state
62     * @param handler A handler to use internally. Used for testing.
63     */
64    public FingerprintGestureDispatcher(IFingerprintService fingerprintService, Object lock,
65            Handler handler) {
66        mFingerprintService = fingerprintService;
67        mLock = lock;
68        mHandler = handler;
69    }
70
71    /**
72     * Update the list of clients that are interested in fingerprint gestures.
73     *
74     * @param clientList The list of potential clients.
75     */
76    public void updateClientList(List<? extends FingerprintGestureClient> clientList) {
77        synchronized (mLock) {
78            mCapturingClients.clear();
79            for (int i = 0; i < clientList.size(); i++) {
80                FingerprintGestureClient client = clientList.get(i);
81                if (client.isCapturingFingerprintGestures()) {
82                    mCapturingClients.add(client);
83                }
84            }
85            if (mCapturingClients.isEmpty()) {
86                if (mRegisteredReadOnlyExceptInHandler) {
87                    mHandler.obtainMessage(MSG_UNREGISTER).sendToTarget();
88                }
89            } else {
90                if(!mRegisteredReadOnlyExceptInHandler) {
91                    mHandler.obtainMessage(MSG_REGISTER).sendToTarget();
92                }
93            }
94        }
95    }
96
97    @Override
98    public void onClientActiveChanged(boolean nonGestureFingerprintClientActive) {
99        synchronized (mLock) {
100            for (int i = 0; i < mCapturingClients.size(); i++) {
101                mCapturingClients.get(i).onFingerprintGestureDetectionActiveChanged(
102                        !nonGestureFingerprintClientActive);
103            }
104        }
105    }
106
107    public boolean isFingerprintGestureDetectionAvailable() {
108        long identity = Binder.clearCallingIdentity();
109        try {
110            return !mFingerprintService.isClientActive();
111        } catch (RemoteException re) {
112            return false;
113        } finally {
114            Binder.restoreCallingIdentity(identity);
115        }
116    }
117
118    /**
119     * Called when the fingerprint sensor detects a gesture
120     *
121     * @param fingerprintKeyCode
122     * @return {@code true} if the gesture is consumed. {@code false} otherwise.
123     */
124    public boolean onFingerprintGesture(int fingerprintKeyCode) {
125        int idForFingerprintGestureManager;
126
127        final List<FingerprintGestureClient> clientList;
128        synchronized (mLock) {
129            if (mCapturingClients.isEmpty()) {
130                return false;
131            }
132            switch (fingerprintKeyCode) {
133                case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP:
134                    idForFingerprintGestureManager =
135                            FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_UP;
136                    break;
137                case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN:
138                    idForFingerprintGestureManager =
139                            FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_DOWN;
140                    break;
141                case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT:
142                    idForFingerprintGestureManager =
143                            FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_RIGHT;
144                    break;
145                case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT:
146                    idForFingerprintGestureManager =
147                            FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_LEFT;
148                    break;
149                default:
150                    return false;
151            }
152            clientList = new ArrayList<>(mCapturingClients);
153        }
154        for (int i = 0; i < clientList.size(); i++) {
155            clientList.get(i).onFingerprintGesture(idForFingerprintGestureManager);
156        }
157        return true;
158    }
159
160    @Override
161    public boolean handleMessage(Message message) {
162        if (message.what == MSG_REGISTER) {
163            long identity = Binder.clearCallingIdentity();
164            try {
165                mFingerprintService.addClientActiveCallback(this);
166                mRegisteredReadOnlyExceptInHandler = true;
167            } catch (RemoteException re) {
168                Slog.e(LOG_TAG, "Failed to register for fingerprint activity callbacks");
169            } finally {
170                Binder.restoreCallingIdentity(identity);
171            }
172            return false;
173        } else if (message.what == MSG_UNREGISTER) {
174            long identity = Binder.clearCallingIdentity();
175            try {
176                mFingerprintService.removeClientActiveCallback(this);
177            } catch (RemoteException re) {
178                Slog.e(LOG_TAG, "Failed to unregister for fingerprint activity callbacks");
179            } finally {
180                Binder.restoreCallingIdentity(identity);
181            }
182            mRegisteredReadOnlyExceptInHandler = false;
183        } else {
184            Slog.e(LOG_TAG, "Unknown message: " + message.what);
185            return false;
186        }
187        return true;
188    }
189
190    // Interface for potential clients.
191    public interface FingerprintGestureClient {
192        /**
193         * @return {@code true} if the client is capturing fingerprint gestures
194         */
195        boolean isCapturingFingerprintGestures();
196
197        /**
198         * Callback when gesture detection becomes active or inactive.
199         *
200         * @param active {@code true} when detection is active
201         */
202        void onFingerprintGestureDetectionActiveChanged(boolean active);
203
204        /**
205         * Callback when gesture is detected
206         *
207         * @param gesture The identifier for the gesture. For example,
208         * {@link FingerprintGestureController#FINGERPRINT_GESTURE_SWIPE_LEFT}
209         */
210        void onFingerprintGesture(int gesture);
211    }
212}
213