1 /*
2  * Copyright (C) 2022 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 
17 package com.android.server.biometrics.sensors;
18 
19 
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.hardware.biometrics.IBiometricAuthenticator;
23 import android.hardware.biometrics.IBiometricService;
24 import android.hardware.biometrics.SensorPropertiesInternal;
25 import android.os.Handler;
26 import android.os.IInterface;
27 import android.os.Process;
28 import android.os.RemoteCallbackList;
29 import android.os.RemoteException;
30 import android.util.Pair;
31 import android.util.Slog;
32 
33 import com.android.internal.annotations.VisibleForTesting;
34 import com.android.server.ServiceThread;
35 
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.List;
39 import java.util.function.Supplier;
40 
41 /**
42  * Container for all BiometricServiceProvider implementations.
43  *
44  * @param <T> The service provider type.
45  * @param <P> The internal properties type.
46  * @param <C> The registration callback for {@link #invokeRegisteredCallback(IInterface, List)}.
47  */
48 public abstract class BiometricServiceRegistry<T extends BiometricServiceProvider<P>,
49         P extends SensorPropertiesInternal,
50         C extends IInterface> {
51 
52     private static final String TAG = "BiometricServiceRegistry";
53 
54     // Volatile so they can be read without a lock once all services are registered.
55     // But, ideally remove this and provide immutable copies via the callback instead.
56     @Nullable
57     private volatile List<T> mServiceProviders;
58     @Nullable
59     private volatile List<P> mAllProps;
60 
61     @NonNull
62     private final Supplier<IBiometricService> mBiometricServiceSupplier;
63     @NonNull
64     private final RemoteCallbackList<C> mRegisteredCallbacks = new RemoteCallbackList<>();
65 
BiometricServiceRegistry(@onNull Supplier<IBiometricService> biometricSupplier)66     public BiometricServiceRegistry(@NonNull Supplier<IBiometricService> biometricSupplier) {
67         mBiometricServiceSupplier = biometricSupplier;
68     }
69 
70     /**
71      * Register an implementation by creating a new authenticator and initializing it via
72      * {@link IBiometricService#registerAuthenticator(int, int, int, IBiometricAuthenticator)}
73      * using the given properties.
74      *
75      * @param service service to register with
76      * @param props   internal properties to initialize the authenticator
77      */
registerService(@onNull IBiometricService service, @NonNull P props)78     protected abstract void registerService(@NonNull IBiometricService service, @NonNull P props);
79 
80     /**
81      * Invoke the callback to notify clients that all authenticators have been registered.
82      *
83      * @param callback callback to invoke
84      * @param allProps properties of all authenticators
85      */
invokeRegisteredCallback(@onNull C callback, @NonNull List<P> allProps)86     protected abstract void invokeRegisteredCallback(@NonNull C callback,
87             @NonNull List<P> allProps) throws RemoteException;
88 
89     /**
90      * Register all authenticators in a background thread.
91      *
92      * @param serviceProvider Supplier function that will be invoked on the background thread.
93      */
registerAll(Supplier<List<T>> serviceProvider)94     public void registerAll(Supplier<List<T>> serviceProvider) {
95         // Some HAL might not be started before the system service and will cause the code below
96         // to wait, and some of the operations below might take a significant amount of time to
97         // complete (calls to the HALs). To avoid blocking the rest of system server we put
98         // this on a background thread.
99         final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
100                 true /* allowIo */);
101         thread.start();
102         final Handler handler = new Handler(thread.getLooper());
103         handler.post(() -> registerAllInBackground(serviceProvider));
104         thread.quitSafely();
105     }
106 
107     /** Register authenticators now, only called by {@link #registerAll(Supplier).} */
108     @VisibleForTesting
registerAllInBackground(Supplier<List<T>> serviceProvider)109     public void registerAllInBackground(Supplier<List<T>> serviceProvider) {
110         List<T> providers = serviceProvider.get();
111         if (providers == null) {
112             providers = new ArrayList<>();
113         }
114 
115         final IBiometricService biometricService = mBiometricServiceSupplier.get();
116         if (biometricService == null) {
117             throw new IllegalStateException("biometric service cannot be null");
118         }
119 
120         // Register each sensor individually with BiometricService
121         final List<P> allProps = new ArrayList<>();
122         for (T provider : providers) {
123             final List<P> props = provider.getSensorProperties();
124             for (P prop : props) {
125                 registerService(biometricService, prop);
126             }
127             allProps.addAll(props);
128         }
129 
130         finishRegistration(providers, allProps);
131     }
132 
finishRegistration( @onNull List<T> providers, @NonNull List<P> allProps)133     private synchronized void finishRegistration(
134             @NonNull List<T> providers, @NonNull List<P> allProps) {
135         mServiceProviders = Collections.unmodifiableList(providers);
136         mAllProps = Collections.unmodifiableList(allProps);
137         broadcastAllAuthenticatorsRegistered();
138     }
139 
140     /**
141      * Add a callback that will be invoked once the work from {@link #registerAll(Supplier)}
142      * has finished registering all providers (executes immediately if already done).
143      *
144      * @param callback registration callback
145      */
addAllRegisteredCallback(@ullable C callback)146     public synchronized void addAllRegisteredCallback(@Nullable C callback) {
147         if (callback == null) {
148             Slog.e(TAG, "addAllRegisteredCallback, callback is null");
149             return;
150         }
151 
152         final boolean registered = mRegisteredCallbacks.register(callback);
153         final boolean allRegistered = mServiceProviders != null;
154         if (registered && allRegistered) {
155             broadcastAllAuthenticatorsRegistered();
156         } else if (!registered) {
157             Slog.e(TAG, "addAllRegisteredCallback failed to register callback");
158         }
159     }
160 
broadcastAllAuthenticatorsRegistered()161     private synchronized void broadcastAllAuthenticatorsRegistered() {
162         final int n = mRegisteredCallbacks.beginBroadcast();
163         for (int i = 0; i < n; ++i) {
164             final C cb = mRegisteredCallbacks.getBroadcastItem(i);
165             try {
166                 invokeRegisteredCallback(cb, mAllProps);
167             } catch (RemoteException e) {
168                 Slog.e(TAG, "Remote exception in broadcastAllAuthenticatorsRegistered", e);
169             } finally {
170                 mRegisteredCallbacks.unregister(cb);
171             }
172         }
173         mRegisteredCallbacks.finishBroadcast();
174     }
175 
176     /**
177      * Get a list of registered providers.
178      *
179      * Undefined until {@link #registerAll(Supplier)} has fired the completion callback.
180      */
181     @NonNull
getProviders()182     public List<T> getProviders() {
183         return mServiceProviders != null ? mServiceProviders : Collections.emptyList();
184     }
185 
186     /**
187      * Gets the provider for given sensor id or null if not registered.
188      *
189      * Undefined until {@link #registerAll(Supplier)} has fired the completion callback.
190      */
191     @Nullable
getProviderForSensor(int sensorId)192     public T getProviderForSensor(int sensorId) {
193         if (mServiceProviders != null) {
194             for (T provider : mServiceProviders) {
195                 if (provider.containsSensor(sensorId)) {
196                     return provider;
197                 }
198             }
199         }
200         return null;
201     }
202 
203     /**
204      * Finds the provider for devices with only a single sensor.
205      *
206      * If no providers returns null. If multiple sensors are present this method
207      * will return the first one that is found (this is a legacy for test devices that
208      * use aidl/hidl concurrently and should not occur on real devices).
209      *
210      * Undefined until {@link #registerAll(Supplier)} has fired the completion callback.
211      */
212     @Nullable
getSingleProvider()213     public Pair<Integer, T> getSingleProvider() {
214         if (mAllProps == null || mAllProps.isEmpty()) {
215             Slog.e(TAG, "No sensors found");
216             return null;
217         }
218 
219         // TODO(b/242837110): remove the try-catch once the bug is fixed.
220         try {
221             if (mAllProps.size() > 1) {
222                 Slog.e(TAG, "getSingleProvider() called but multiple sensors present: "
223                         + mAllProps.size());
224             }
225 
226             final int sensorId = mAllProps.get(0).sensorId;
227             final T provider = getProviderForSensor(sensorId);
228             if (provider != null) {
229                 return new Pair<>(sensorId, provider);
230             }
231 
232             Slog.e(TAG, "Single sensor: " + sensorId + ", but provider not found");
233             return null;
234         } catch (NullPointerException e) {
235             final String extra;
236             if (mAllProps == null) {
237                 extra = "mAllProps: null";
238             } else {
239                 extra = "mAllProps.size(): " + mAllProps.size();
240             }
241             Slog.e(TAG, "This shouldn't happen. " + extra, e);
242             throw e;
243         }
244     }
245 
246     /**
247      * Get the properties for all providers.
248      *
249      * Undefined until {@link #registerAll(Supplier)} has fired the completion callback.
250      */
251     @NonNull
getAllProperties()252     public List<P> getAllProperties() {
253         return mAllProps != null ? mAllProps : Collections.emptyList();
254     }
255 }
256