1 /*
2  * Copyright (C) 2021 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.hardware.soundtrigger3.ISoundTriggerHw;
21 import android.hardware.soundtrigger3.ISoundTriggerHwCallback;
22 import android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback;
23 import android.media.soundtrigger.ModelParameterRange;
24 import android.media.soundtrigger.PhraseRecognitionEvent;
25 import android.media.soundtrigger.PhraseSoundModel;
26 import android.media.soundtrigger.Properties;
27 import android.media.soundtrigger.RecognitionConfig;
28 import android.media.soundtrigger.RecognitionEvent;
29 import android.media.soundtrigger.RecognitionStatus;
30 import android.media.soundtrigger.SoundModel;
31 import android.media.soundtrigger.Status;
32 import android.media.soundtrigger_middleware.PhraseRecognitionEventSys;
33 import android.media.soundtrigger_middleware.RecognitionEventSys;
34 import android.os.IBinder;
35 import android.os.RemoteException;
36 import android.os.ServiceSpecificException;
37 import android.os.SystemClock;
38 
39 public class SoundTriggerHw3Compat implements ISoundTriggerHal {
40     private final @NonNull ISoundTriggerHw mDriver;
41     private final @NonNull Runnable mRebootRunnable;
42 
SoundTriggerHw3Compat(@onNull IBinder binder, @NonNull Runnable rebootRunnable)43     public SoundTriggerHw3Compat(@NonNull IBinder binder, @NonNull Runnable rebootRunnable) {
44         mDriver = android.hardware.soundtrigger3.ISoundTriggerHw.Stub.asInterface(binder);
45         mRebootRunnable = rebootRunnable;
46     }
47 
48     @Override
getProperties()49     public Properties getProperties() {
50         try {
51             return mDriver.getProperties();
52         } catch (RemoteException e) {
53             throw e.rethrowAsRuntimeException();
54         }
55     }
56 
57     @Override
registerCallback(GlobalCallback callback)58     public void registerCallback(GlobalCallback callback) {
59         try {
60             mDriver.registerGlobalCallback(new GlobalCallbackAdaper(callback));
61         } catch (RemoteException e) {
62             throw e.rethrowAsRuntimeException();
63         }
64     }
65 
66     @Override
loadSoundModel(SoundModel soundModel, ModelCallback callback)67     public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
68         try {
69             return mDriver.loadSoundModel(soundModel, new ModelCallbackAdaper(callback));
70         } catch (RemoteException e) {
71             throw e.rethrowAsRuntimeException();
72         } catch (ServiceSpecificException e) {
73             if (e.errorCode == Status.RESOURCE_CONTENTION) {
74                 throw new RecoverableException(Status.RESOURCE_CONTENTION);
75             }
76             throw e;
77         }
78     }
79 
80     @Override
loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback)81     public int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback) {
82         try {
83             return mDriver.loadPhraseSoundModel(soundModel, new ModelCallbackAdaper(callback));
84         } catch (RemoteException e) {
85             throw e.rethrowAsRuntimeException();
86         } catch (ServiceSpecificException e) {
87             if (e.errorCode == Status.RESOURCE_CONTENTION) {
88                 throw new RecoverableException(Status.RESOURCE_CONTENTION);
89             }
90             throw e;
91         }
92     }
93 
94     @Override
unloadSoundModel(int modelHandle)95     public void unloadSoundModel(int modelHandle) {
96         try {
97             mDriver.unloadSoundModel(modelHandle);
98         } catch (RemoteException e) {
99             throw e.rethrowAsRuntimeException();
100         }
101     }
102 
103     @Override
startRecognition(int modelHandle, int deviceHandle, int ioHandle, RecognitionConfig config)104     public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
105             RecognitionConfig config) {
106         try {
107             mDriver.startRecognition(modelHandle, deviceHandle, ioHandle, config);
108         } catch (RemoteException e) {
109             throw e.rethrowAsRuntimeException();
110         } catch (ServiceSpecificException e) {
111             if (e.errorCode == Status.RESOURCE_CONTENTION) {
112                 throw new RecoverableException(Status.RESOURCE_CONTENTION);
113             }
114             throw e;
115         }
116     }
117 
118     @Override
stopRecognition(int modelHandle)119     public void stopRecognition(int modelHandle) {
120         try {
121             mDriver.stopRecognition(modelHandle);
122         } catch (RemoteException e) {
123             throw e.rethrowAsRuntimeException();
124         }
125     }
126 
127     @Override
forceRecognitionEvent(int modelHandle)128     public void forceRecognitionEvent(int modelHandle) {
129         try {
130             mDriver.forceRecognitionEvent(modelHandle);
131         } catch (RemoteException e) {
132             throw e.rethrowAsRuntimeException();
133         }
134     }
135 
136     @Override
queryParameter(int modelHandle, int param)137     public ModelParameterRange queryParameter(int modelHandle, int param) {
138         try {
139             return mDriver.queryParameter(modelHandle, param);
140         } catch (RemoteException e) {
141             throw e.rethrowAsRuntimeException();
142         }
143     }
144 
145     @Override
getModelParameter(int modelHandle, int param)146     public int getModelParameter(int modelHandle, int param) {
147         try {
148             return mDriver.getParameter(modelHandle, param);
149         } catch (RemoteException e) {
150             throw e.rethrowAsRuntimeException();
151         }
152     }
153 
154     @Override
setModelParameter(int modelHandle, int param, int value)155     public void setModelParameter(int modelHandle, int param, int value) {
156         try {
157             mDriver.setParameter(modelHandle, param, value);
158         } catch (RemoteException e) {
159             throw e.rethrowAsRuntimeException();
160         }
161     }
162 
163     @Override
interfaceDescriptor()164     public String interfaceDescriptor() {
165         try {
166             return mDriver.asBinder().getInterfaceDescriptor();
167         } catch (RemoteException e) {
168             throw e.rethrowAsRuntimeException();
169         }
170     }
171 
172     @Override
linkToDeath(IBinder.DeathRecipient recipient)173     public void linkToDeath(IBinder.DeathRecipient recipient) {
174         try {
175             mDriver.asBinder().linkToDeath(recipient, 0);
176         } catch (RemoteException e) {
177             throw e.rethrowAsRuntimeException();
178         }
179     }
180 
181     @Override
unlinkToDeath(IBinder.DeathRecipient recipient)182     public void unlinkToDeath(IBinder.DeathRecipient recipient) {
183         mDriver.asBinder().unlinkToDeath(recipient, 0);
184     }
185 
186     @Override
flushCallbacks()187     public void flushCallbacks() {
188         // No-op.
189     }
190 
191     @Override
clientAttached(IBinder binder)192     public void clientAttached(IBinder binder) {
193         // No-op. This method is for test purposes, and is intercepted above.
194     }
195 
196     @Override
clientDetached(IBinder binder)197     public void clientDetached(IBinder binder) {
198         // No-op. This method is for test purposes, and is intercepted above.
199     }
200 
201     @Override
reboot()202     public void reboot() {
203         mRebootRunnable.run();
204     }
205 
206     @Override
detach()207     public void detach() {
208         // No-op.
209     }
210 
211     private static class GlobalCallbackAdaper extends ISoundTriggerHwGlobalCallback.Stub {
212         private final @NonNull GlobalCallback mDelegate;
213 
GlobalCallbackAdaper(@onNull GlobalCallback callback)214         public GlobalCallbackAdaper(@NonNull GlobalCallback callback) {
215             mDelegate = callback;
216         }
217 
218         @Override
onResourcesAvailable()219         public void onResourcesAvailable() {
220             mDelegate.onResourcesAvailable();
221         }
222 
223         @Override
getInterfaceVersion()224         public int getInterfaceVersion() {
225             return ISoundTriggerHwGlobalCallback.VERSION;
226         }
227 
228         @Override
getInterfaceHash()229         public String getInterfaceHash() {
230             return ISoundTriggerHwGlobalCallback.HASH;
231         }
232     }
233 
234     private static class ModelCallbackAdaper extends ISoundTriggerHwCallback.Stub {
235         private final @NonNull ModelCallback mDelegate;
236 
ModelCallbackAdaper(ModelCallback callback)237         public ModelCallbackAdaper(ModelCallback callback) {
238             mDelegate = callback;
239         }
240 
241         @Override
modelUnloaded(int model)242         public void modelUnloaded(int model) {
243             mDelegate.modelUnloaded(model);
244         }
245 
246         @Override
phraseRecognitionCallback(int model, PhraseRecognitionEvent event)247         public void phraseRecognitionCallback(int model, PhraseRecognitionEvent event) {
248             // A FORCED status implies that recognition is still active after the event.
249             event.common.recognitionStillActive |= event.common.status == RecognitionStatus.FORCED;
250             PhraseRecognitionEventSys phraseRecognitionEventSys = new PhraseRecognitionEventSys();
251             phraseRecognitionEventSys.phraseRecognitionEvent = event;
252             phraseRecognitionEventSys.halEventReceivedMillis = SystemClock.elapsedRealtimeNanos();
253             mDelegate.phraseRecognitionCallback(model, phraseRecognitionEventSys);
254         }
255 
256         @Override
recognitionCallback(int model, RecognitionEvent event)257         public void recognitionCallback(int model, RecognitionEvent event) {
258             // A FORCED status implies that recognition is still active after the event.
259             event.recognitionStillActive |= event.status == RecognitionStatus.FORCED;
260             RecognitionEventSys recognitionEventSys = new RecognitionEventSys();
261             recognitionEventSys.recognitionEvent = event;
262             recognitionEventSys.halEventReceivedMillis = SystemClock.elapsedRealtimeNanos();
263             mDelegate.recognitionCallback(model, recognitionEventSys);
264         }
265 
266         @Override
getInterfaceVersion()267         public int getInterfaceVersion() {
268             return ISoundTriggerHwCallback.VERSION;
269         }
270 
271         @Override
getInterfaceHash()272         public String getInterfaceHash() {
273             return ISoundTriggerHwCallback.HASH;
274         }
275     }
276 }
277