1 /*
2  * Copyright (C) 2019 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.soundtrigger_middleware;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
22 import android.media.soundtrigger.ModelParameterRange;
23 import android.media.soundtrigger.PhraseSoundModel;
24 import android.media.soundtrigger.Properties;
25 import android.media.soundtrigger.RecognitionConfig;
26 import android.media.soundtrigger.SoundModel;
27 import android.media.soundtrigger.Status;
28 import android.media.soundtrigger_middleware.PhraseRecognitionEventSys;
29 import android.media.soundtrigger_middleware.RecognitionEventSys;
30 import android.os.IBinder;
31 import android.os.IHwBinder;
32 import android.os.RemoteException;
33 import android.os.SystemClock;
34 import android.system.OsConstants;
35 import android.util.Slog;
36 
37 import java.io.IOException;
38 import java.util.HashMap;
39 import java.util.Map;
40 import java.util.Objects;
41 import java.util.concurrent.ConcurrentHashMap;
42 import java.util.concurrent.ConcurrentMap;
43 import java.util.concurrent.atomic.AtomicInteger;
44 import java.util.concurrent.atomic.AtomicReference;
45 
46 /**
47  * An implementation of {@link ISoundTriggerHal}, on top of any
48  * android.hardware.soundtrigger.V2_x.ISoundTriggerHw implementation. This class hides away some of
49  * the details involved with retaining backward compatibility and adapts to the more pleasant syntax
50  * exposed by {@link ISoundTriggerHal}, compared to the bare driver interface.
51  * <p>
52  * Exception handling:
53  * <ul>
54  * <li>All {@link RemoteException}s get rethrown as {@link RuntimeException}.
55  * <li>All HAL malfunctions get thrown as {@link HalException}.
56  * <li>All unsupported operations get thrown as {@link RecoverableException} with a
57  * {@link android.media.soundtrigger.Status#OPERATION_NOT_SUPPORTED}
58  * code.
59  * </ul>
60  */
61 final class SoundTriggerHw2Compat implements ISoundTriggerHal {
62     private static final String TAG = "SoundTriggerHw2Compat";
63 
64     private final @NonNull Runnable mRebootRunnable;
65     private final @NonNull IHwBinder mBinder;
66     private @NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw mUnderlying_2_0;
67     private @Nullable android.hardware.soundtrigger.V2_1.ISoundTriggerHw mUnderlying_2_1;
68     private @Nullable android.hardware.soundtrigger.V2_2.ISoundTriggerHw mUnderlying_2_2;
69     private @Nullable android.hardware.soundtrigger.V2_3.ISoundTriggerHw mUnderlying_2_3;
70 
71     // HAL <=2.1 requires us to pass a callback argument to startRecognition. We will store the one
72     // passed on load and then pass it on start. We don't bother storing the callback on newer
73     // versions.
74     private final @NonNull ConcurrentMap<Integer, ModelCallback> mModelCallbacks =
75             new ConcurrentHashMap<>();
76 
77     // A map from IBinder.DeathRecipient to IHwBinder.DeathRecipient for doing the mapping upon
78     // unlinking.
79     private final @NonNull Map<IBinder.DeathRecipient, IHwBinder.DeathRecipient>
80             mDeathRecipientMap = new HashMap<>();
81 
82     // The properties are read at construction time and cached, since we need to use some of them
83     // to enforce constraints.
84     private final @NonNull Properties mProperties;
85 
create( @onNull ISoundTriggerHw underlying, @NonNull Runnable rebootRunnable, ICaptureStateNotifier notifier)86     static ISoundTriggerHal create(
87             @NonNull ISoundTriggerHw underlying,
88             @NonNull Runnable rebootRunnable,
89             ICaptureStateNotifier notifier) {
90         return create(underlying.asBinder(), rebootRunnable, notifier);
91     }
92 
create(@onNull IHwBinder binder, @NonNull Runnable rebootRunnable, ICaptureStateNotifier notifier)93     static ISoundTriggerHal create(@NonNull IHwBinder binder,
94             @NonNull Runnable rebootRunnable,
95             ICaptureStateNotifier notifier) {
96         SoundTriggerHw2Compat compat = new SoundTriggerHw2Compat(binder, rebootRunnable);
97         ISoundTriggerHal result = compat;
98         // Add max model limiter for versions.
99         result = new SoundTriggerHalMaxModelLimiter(result, compat.mProperties.maxSoundModels);
100         // Add concurrent capture handler for HALs which do not support concurrent capture.
101         if (!compat.mProperties.concurrentCapture) {
102             result = new SoundTriggerHalConcurrentCaptureHandler(result, notifier);
103         }
104         return result;
105     }
106 
SoundTriggerHw2Compat(@onNull IHwBinder binder, @NonNull Runnable rebootRunnable)107     private SoundTriggerHw2Compat(@NonNull IHwBinder binder, @NonNull Runnable rebootRunnable) {
108         mRebootRunnable = Objects.requireNonNull(rebootRunnable);
109         mBinder = Objects.requireNonNull(binder);
110         initUnderlying(binder);
111         mProperties = Objects.requireNonNull(getPropertiesInternal());
112     }
113 
initUnderlying(IHwBinder binder)114     private void initUnderlying(IHwBinder binder) {
115         // We want to share the proxy instances rather than create a separate proxy for every
116         // version, so we go down the versions in descending order to find the latest one supported,
117         // and then simply up-cast it to obtain all the versions that are earlier.
118 
119         // Attempt 2.3
120         android.hardware.soundtrigger.V2_3.ISoundTriggerHw as2_3 =
121                 android.hardware.soundtrigger.V2_3.ISoundTriggerHw.asInterface(binder);
122         if (as2_3 != null) {
123             mUnderlying_2_0 = mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = as2_3;
124             return;
125         }
126 
127         // Attempt 2.2
128         android.hardware.soundtrigger.V2_2.ISoundTriggerHw as2_2 =
129                 android.hardware.soundtrigger.V2_2.ISoundTriggerHw.asInterface(binder);
130         if (as2_2 != null) {
131             mUnderlying_2_0 = mUnderlying_2_1 = mUnderlying_2_2 = as2_2;
132             mUnderlying_2_3 = null;
133             return;
134         }
135 
136         // Attempt 2.1
137         android.hardware.soundtrigger.V2_1.ISoundTriggerHw as2_1 =
138                 android.hardware.soundtrigger.V2_1.ISoundTriggerHw.asInterface(binder);
139         if (as2_1 != null) {
140             mUnderlying_2_0 = mUnderlying_2_1 = as2_1;
141             mUnderlying_2_2 = mUnderlying_2_3 = null;
142             return;
143         }
144 
145         // Attempt 2.0
146         android.hardware.soundtrigger.V2_0.ISoundTriggerHw as2_0 =
147                 android.hardware.soundtrigger.V2_0.ISoundTriggerHw.asInterface(binder);
148         if (as2_0 != null) {
149             mUnderlying_2_0 = as2_0;
150             mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = null;
151             return;
152         }
153 
154         throw new RuntimeException("Binder doesn't support ISoundTriggerHw@2.0");
155     }
156 
handleHalStatus(int status, String methodName)157     private static void handleHalStatus(int status, String methodName) {
158         if (status != 0) {
159             throw new HalException(status, methodName);
160         }
161     }
162 
handleHalStatusAllowBusy(int status, String methodName)163     private static void handleHalStatusAllowBusy(int status, String methodName) {
164         if (status == -OsConstants.EBUSY) {
165             throw new RecoverableException(Status.RESOURCE_CONTENTION);
166         }
167         handleHalStatus(status, methodName);
168     }
169 
170     @Override
reboot()171     public void reboot() {
172         mRebootRunnable.run();
173     }
174 
175     @Override
detach()176     public void detach() {
177         // No-op.
178     }
179 
180     @Override
getProperties()181     public Properties getProperties() {
182         return mProperties;
183     }
184 
getPropertiesInternal()185     private Properties getPropertiesInternal() {
186         try {
187             AtomicInteger retval = new AtomicInteger(-1);
188             AtomicReference<android.hardware.soundtrigger.V2_3.Properties> properties =
189                     new AtomicReference<>();
190             try {
191                 as2_3().getProperties_2_3(
192                         (r, p) -> {
193                             retval.set(r);
194                             properties.set(p);
195                         });
196             } catch (NotSupported e) {
197                 // Fall-back to the 2.0 version:
198                 return getProperties_2_0();
199             }
200             handleHalStatus(retval.get(), "getProperties_2_3");
201             return ConversionUtil.hidl2aidlProperties(properties.get());
202         } catch (RemoteException e) {
203             throw e.rethrowAsRuntimeException();
204         }
205     }
206 
207     @Override
registerCallback(GlobalCallback callback)208     public void registerCallback(GlobalCallback callback) {
209         // In versions 2.x the events represented by this callback don't exist, we can
210         // safely ignore this.
211     }
212 
213     @Override
loadSoundModel(SoundModel soundModel, ModelCallback callback)214     public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
215         android.hardware.soundtrigger.V2_3.ISoundTriggerHw.SoundModel hidlModel =
216                 ConversionUtil.aidl2hidlSoundModel(soundModel);
217         try {
218             AtomicInteger retval = new AtomicInteger(-1);
219             AtomicInteger handle = new AtomicInteger(0);
220             try {
221                 as2_1().loadSoundModel_2_1(hidlModel, new ModelCallbackWrapper(callback),
222                         0,
223                         (r, h) -> {
224                             retval.set(r);
225                             handle.set(h);
226                         });
227                 handleHalStatus(retval.get(), "loadSoundModel_2_1");
228                 mModelCallbacks.put(handle.get(), callback);
229             } catch (NotSupported ee) {
230                 // Fall-back to the 2.0 version:
231                 return loadSoundModel_2_0(hidlModel, callback);
232             }
233             return handle.get();
234         } catch (RemoteException e) {
235             throw e.rethrowAsRuntimeException();
236         } finally {
237             // TODO(b/219825762): We should be able to use the entire object in a try-with-resources
238             //   clause, instead of having to explicitly close internal fields.
239             if (hidlModel.data != null) {
240                 try {
241                     hidlModel.data.close();
242                 } catch (IOException e) {
243                     Slog.e(TAG, "Failed to close file", e);
244                 }
245             }
246         }
247     }
248 
249     @Override
loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback)250     public int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback) {
251         android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel hidlModel =
252                 ConversionUtil.aidl2hidlPhraseSoundModel(soundModel);
253         try {
254             AtomicInteger retval = new AtomicInteger(-1);
255             AtomicInteger handle = new AtomicInteger(0);
256             try {
257                 as2_1().loadPhraseSoundModel_2_1(hidlModel, new ModelCallbackWrapper(callback),
258                         0,
259                         (r, h) -> {
260                             retval.set(r);
261                             handle.set(h);
262                         });
263                 handleHalStatus(retval.get(), "loadPhraseSoundModel_2_1");
264                 mModelCallbacks.put(handle.get(), callback);
265             } catch (NotSupported ee) {
266                 // Fall-back to the 2.0 version:
267                 return loadPhraseSoundModel_2_0(hidlModel, callback);
268             }
269             return handle.get();
270         } catch (RemoteException e) {
271             throw e.rethrowAsRuntimeException();
272         } finally {
273             // TODO(b/219825762): We should be able to use the entire object in a try-with-resources
274             //   clause, instead of having to explicitly close internal fields.
275             if (hidlModel.common.data != null) {
276                 try {
277                     hidlModel.common.data.close();
278                 } catch (IOException e) {
279                     Slog.e(TAG, "Failed to close file", e);
280                 }
281             }
282         }
283     }
284 
285     @Override
unloadSoundModel(int modelHandle)286     public void unloadSoundModel(int modelHandle) {
287         try {
288             // Safe if key doesn't exist.
289             mModelCallbacks.remove(modelHandle);
290             int retval = as2_0().unloadSoundModel(modelHandle);
291             handleHalStatus(retval, "unloadSoundModel");
292         } catch (RemoteException e) {
293             throw e.rethrowAsRuntimeException();
294         }
295     }
296 
297     @Override
stopRecognition(int modelHandle)298     public void stopRecognition(int modelHandle) {
299         try {
300             int retval = as2_0().stopRecognition(modelHandle);
301             handleHalStatus(retval, "stopRecognition");
302         } catch (RemoteException e) {
303             throw e.rethrowAsRuntimeException();
304         }
305 
306     }
307 
308     @Override
startRecognition(int modelHandle, int deviceHandle, int ioHandle, RecognitionConfig config)309     public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
310             RecognitionConfig config) {
311         android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig =
312                 ConversionUtil.aidl2hidlRecognitionConfig(config, deviceHandle, ioHandle);
313         try {
314             try {
315                 int retval = as2_3().startRecognition_2_3(modelHandle, hidlConfig);
316                 handleHalStatus(retval, "startRecognition_2_3");
317             } catch (NotSupported ee) {
318                 // Fall-back to the 2.0 version:
319                 startRecognition_2_1(modelHandle, hidlConfig);
320             }
321         } catch (RemoteException e) {
322             throw e.rethrowAsRuntimeException();
323         }
324     }
325 
326     @Override
forceRecognitionEvent(int modelHandle)327     public void forceRecognitionEvent(int modelHandle) {
328         try {
329             int retval = as2_2().getModelState(modelHandle);
330             handleHalStatus(retval, "getModelState");
331         } catch (RemoteException e) {
332             throw e.rethrowAsRuntimeException();
333         } catch (NotSupported e) {
334             throw e.throwAsRecoverableException();
335         }
336     }
337 
338     @Override
getModelParameter(int modelHandle, int param)339     public int getModelParameter(int modelHandle, int param) {
340         AtomicInteger status = new AtomicInteger(-1);
341         AtomicInteger value = new AtomicInteger(0);
342         try {
343             as2_3().getParameter(modelHandle, param,
344                     (s, v) -> {
345                         status.set(s);
346                         value.set(v);
347                     });
348         } catch (RemoteException e) {
349             throw e.rethrowAsRuntimeException();
350         } catch (NotSupported e) {
351             throw e.throwAsRecoverableException();
352         }
353         handleHalStatus(status.get(), "getParameter");
354         return value.get();
355     }
356 
357     @Override
setModelParameter(int modelHandle, int param, int value)358     public void setModelParameter(int modelHandle, int param, int value) {
359         try {
360             int retval = as2_3().setParameter(modelHandle, param, value);
361             handleHalStatus(retval, "setParameter");
362         } catch (RemoteException e) {
363             throw e.rethrowAsRuntimeException();
364         } catch (NotSupported e) {
365             throw e.throwAsRecoverableException();
366         }
367     }
368 
369     @Override
queryParameter(int modelHandle, int param)370     public ModelParameterRange queryParameter(int modelHandle, int param) {
371         AtomicInteger status = new AtomicInteger(-1);
372         AtomicReference<android.hardware.soundtrigger.V2_3.OptionalModelParameterRange>
373                 optionalRange =
374                 new AtomicReference<>();
375         try {
376             as2_3().queryParameter(modelHandle, param,
377                     (s, r) -> {
378                         status.set(s);
379                         optionalRange.set(r);
380                     });
381         } catch (NotSupported e) {
382             // For older drivers, we consider no model parameter to be supported.
383             return null;
384         } catch (RemoteException e) {
385             throw e.rethrowAsRuntimeException();
386         }
387         handleHalStatus(status.get(), "queryParameter");
388         return (optionalRange.get().getDiscriminator()
389                 == android.hardware.soundtrigger.V2_3.OptionalModelParameterRange.hidl_discriminator.range)
390                 ?
391                 ConversionUtil.hidl2aidlModelParameterRange(optionalRange.get().range()) : null;
392     }
393 
394     @Override
linkToDeath(IBinder.DeathRecipient recipient)395     public void linkToDeath(IBinder.DeathRecipient recipient) {
396         IHwBinder.DeathRecipient wrapper = cookie -> recipient.binderDied();
397         mDeathRecipientMap.put(recipient, wrapper);
398         mBinder.linkToDeath(wrapper, 0);
399     }
400 
401     @Override
unlinkToDeath(IBinder.DeathRecipient recipient)402     public void unlinkToDeath(IBinder.DeathRecipient recipient) {
403         mBinder.unlinkToDeath(mDeathRecipientMap.remove(recipient));
404     }
405 
406     @Override
interfaceDescriptor()407     public String interfaceDescriptor() {
408         try {
409             return as2_0().interfaceDescriptor();
410         } catch (RemoteException e) {
411             throw e.rethrowAsRuntimeException();
412         }
413     }
414 
415     @Override
flushCallbacks()416     public void flushCallbacks() {
417         // This is a no-op. Only implemented for decorators.
418     }
419 
420     @Override
clientAttached(IBinder binder)421     public void clientAttached(IBinder binder) {
422         // This is a no-op. Only implemented for decorators.
423     }
424 
425     @Override
clientDetached(IBinder binder)426     public void clientDetached(IBinder binder) {
427         // This is a no-op. Only implemented for decorators.
428     }
429 
getProperties_2_0()430     private Properties getProperties_2_0()
431             throws RemoteException {
432         AtomicInteger retval = new AtomicInteger(-1);
433         AtomicReference<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties>
434                 properties =
435                 new AtomicReference<>();
436         as2_0().getProperties(
437                 (r, p) -> {
438                     retval.set(r);
439                     properties.set(p);
440                 });
441         handleHalStatus(retval.get(), "getProperties");
442         return ConversionUtil.hidl2aidlProperties(
443                 Hw2CompatUtil.convertProperties_2_0_to_2_3(properties.get()));
444     }
445 
loadSoundModel_2_0( android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel, ModelCallback callback)446     private int loadSoundModel_2_0(
447             android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
448             ModelCallback callback)
449             throws RemoteException {
450         // Convert the soundModel to V2.0.
451         android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model_2_0 =
452                 Hw2CompatUtil.convertSoundModel_2_1_to_2_0(soundModel);
453 
454         AtomicInteger retval = new AtomicInteger(-1);
455         AtomicInteger handle = new AtomicInteger(0);
456         as2_0().loadSoundModel(model_2_0, new ModelCallbackWrapper(callback), 0, (r, h) -> {
457             retval.set(r);
458             handle.set(h);
459         });
460         handleHalStatus(retval.get(), "loadSoundModel");
461         mModelCallbacks.put(handle.get(), callback);
462         return handle.get();
463     }
464 
loadPhraseSoundModel_2_0( android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel, ModelCallback callback)465     private int loadPhraseSoundModel_2_0(
466             android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel,
467             ModelCallback callback)
468             throws RemoteException {
469         // Convert the soundModel to V2.0.
470         android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model_2_0 =
471                 Hw2CompatUtil.convertPhraseSoundModel_2_1_to_2_0(soundModel);
472 
473         AtomicInteger retval = new AtomicInteger(-1);
474         AtomicInteger handle = new AtomicInteger(0);
475         as2_0().loadPhraseSoundModel(model_2_0, new ModelCallbackWrapper(callback), 0,
476                 (r, h) -> {
477                     retval.set(r);
478                     handle.set(h);
479                 });
480         handleHalStatus(retval.get(), "loadSoundModel");
481         mModelCallbacks.put(handle.get(), callback);
482         return handle.get();
483     }
484 
startRecognition_2_1(int modelHandle, android.hardware.soundtrigger.V2_3.RecognitionConfig config)485     private void startRecognition_2_1(int modelHandle,
486             android.hardware.soundtrigger.V2_3.RecognitionConfig config) {
487         try {
488             try {
489                 android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config_2_1 =
490                         Hw2CompatUtil.convertRecognitionConfig_2_3_to_2_1(config);
491                 int retval = as2_1().startRecognition_2_1(modelHandle, config_2_1,
492                         new ModelCallbackWrapper(mModelCallbacks.get(modelHandle)), 0);
493                 handleHalStatus(retval, "startRecognition_2_1");
494             } catch (NotSupported e) {
495                 // Fall-back to the 2.0 version:
496                 startRecognition_2_0(modelHandle, config);
497             }
498         } catch (RemoteException e) {
499             throw e.rethrowAsRuntimeException();
500         }
501     }
502 
startRecognition_2_0(int modelHandle, android.hardware.soundtrigger.V2_3.RecognitionConfig config)503     private void startRecognition_2_0(int modelHandle,
504             android.hardware.soundtrigger.V2_3.RecognitionConfig config)
505             throws RemoteException {
506         android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig config_2_0 =
507                 Hw2CompatUtil.convertRecognitionConfig_2_3_to_2_0(config);
508         int retval = as2_0().startRecognition(modelHandle, config_2_0,
509                 new ModelCallbackWrapper(mModelCallbacks.get(modelHandle)), 0);
510         handleHalStatus(retval, "startRecognition");
511     }
512 
513     private @NonNull
as2_0()514     android.hardware.soundtrigger.V2_0.ISoundTriggerHw as2_0() {
515         return mUnderlying_2_0;
516     }
517 
518     private @NonNull
as2_1()519     android.hardware.soundtrigger.V2_1.ISoundTriggerHw as2_1() throws NotSupported {
520         if (mUnderlying_2_1 == null) {
521             throw new NotSupported("Underlying driver version < 2.1");
522         }
523         return mUnderlying_2_1;
524     }
525 
526     private @NonNull
as2_2()527     android.hardware.soundtrigger.V2_2.ISoundTriggerHw as2_2() throws NotSupported {
528         if (mUnderlying_2_2 == null) {
529             throw new NotSupported("Underlying driver version < 2.2");
530         }
531         return mUnderlying_2_2;
532     }
533 
534     private @NonNull
as2_3()535     android.hardware.soundtrigger.V2_3.ISoundTriggerHw as2_3() throws NotSupported {
536         if (mUnderlying_2_3 == null) {
537             throw new NotSupported("Underlying driver version < 2.3");
538         }
539         return mUnderlying_2_3;
540     }
541 
542     /**
543      * A checked exception representing the requested interface version not being supported.
544      * At the public interface layer, use {@link #throwAsRecoverableException()} to propagate it to
545      * the caller if the request cannot be fulfilled.
546      */
547     private static class NotSupported extends Exception {
NotSupported(String message)548         NotSupported(String message) {
549             super(message);
550         }
551 
552         /**
553          * Throw this as a recoverable exception.
554          *
555          * @return Never actually returns anything. Always throws. Used so that caller can write
556          * throw e.throwAsRecoverableException().
557          */
throwAsRecoverableException()558         RecoverableException throwAsRecoverableException() {
559             throw new RecoverableException(Status.OPERATION_NOT_SUPPORTED, getMessage());
560         }
561     }
562 
563     private static class ModelCallbackWrapper extends
564             android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.Stub {
565         private final @NonNull ModelCallback mDelegate;
566 
ModelCallbackWrapper( @onNull ModelCallback delegate)567         private ModelCallbackWrapper(
568                 @NonNull ModelCallback delegate) {
569             mDelegate = Objects.requireNonNull(delegate);
570         }
571 
572         @Override
recognitionCallback_2_1( android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event, int cookie)573         public void recognitionCallback_2_1(
574                 android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event,
575                 int cookie) {
576             RecognitionEventSys eventSys = new RecognitionEventSys();
577             eventSys.recognitionEvent = ConversionUtil.hidl2aidlRecognitionEvent(event);
578             eventSys.halEventReceivedMillis = SystemClock.elapsedRealtime();
579             mDelegate.recognitionCallback(event.header.model, eventSys);
580         }
581 
582         @Override
phraseRecognitionCallback_2_1( android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent event, int cookie)583         public void phraseRecognitionCallback_2_1(
584                 android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent event,
585                 int cookie) {
586             PhraseRecognitionEventSys eventSys = new PhraseRecognitionEventSys();
587             eventSys.phraseRecognitionEvent = ConversionUtil.hidl2aidlPhraseRecognitionEvent(event);
588             eventSys.halEventReceivedMillis = SystemClock.elapsedRealtime();
589             mDelegate.phraseRecognitionCallback(event.common.header.model, eventSys);
590         }
591 
592         @Override
soundModelCallback_2_1( android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent event, int cookie)593         public void soundModelCallback_2_1(
594                 android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent event,
595                 int cookie) {
596             // Nobody cares.
597         }
598 
599         @Override
recognitionCallback( android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent event, int cookie)600         public void recognitionCallback(
601                 android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent event,
602                 int cookie) {
603             android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event_2_1 =
604                     Hw2CompatUtil.convertRecognitionEvent_2_0_to_2_1(event);
605             recognitionCallback_2_1(event_2_1, cookie);
606         }
607 
608         @Override
phraseRecognitionCallback( android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent event, int cookie)609         public void phraseRecognitionCallback(
610                 android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent event,
611                 int cookie) {
612             android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent
613                     event_2_1 = Hw2CompatUtil.convertPhraseRecognitionEvent_2_0_to_2_1(event);
614             phraseRecognitionCallback_2_1(event_2_1, cookie);
615         }
616 
617         @Override
soundModelCallback( android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent event, int cookie)618         public void soundModelCallback(
619                 android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent event,
620                 int cookie) {
621             // Nobody cares.
622         }
623     }
624 }
625