/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.soundtrigger_middleware; import android.annotation.NonNull; import android.hardware.soundtrigger3.ISoundTriggerHw; import android.hardware.soundtrigger3.ISoundTriggerHwCallback; import android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback; import android.media.soundtrigger.ModelParameterRange; import android.media.soundtrigger.PhraseRecognitionEvent; import android.media.soundtrigger.PhraseSoundModel; import android.media.soundtrigger.Properties; import android.media.soundtrigger.RecognitionConfig; import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.RecognitionStatus; import android.media.soundtrigger.SoundModel; import android.media.soundtrigger.Status; import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; import android.media.soundtrigger_middleware.RecognitionEventSys; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.SystemClock; public class SoundTriggerHw3Compat implements ISoundTriggerHal { private final @NonNull ISoundTriggerHw mDriver; private final @NonNull Runnable mRebootRunnable; public SoundTriggerHw3Compat(@NonNull IBinder binder, @NonNull Runnable rebootRunnable) { mDriver = android.hardware.soundtrigger3.ISoundTriggerHw.Stub.asInterface(binder); mRebootRunnable = rebootRunnable; } @Override public Properties getProperties() { try { return mDriver.getProperties(); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } @Override public void registerCallback(GlobalCallback callback) { try { mDriver.registerGlobalCallback(new GlobalCallbackAdaper(callback)); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } @Override public int loadSoundModel(SoundModel soundModel, ModelCallback callback) { try { return mDriver.loadSoundModel(soundModel, new ModelCallbackAdaper(callback)); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } catch (ServiceSpecificException e) { if (e.errorCode == Status.RESOURCE_CONTENTION) { throw new RecoverableException(Status.RESOURCE_CONTENTION); } throw e; } } @Override public int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback) { try { return mDriver.loadPhraseSoundModel(soundModel, new ModelCallbackAdaper(callback)); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } catch (ServiceSpecificException e) { if (e.errorCode == Status.RESOURCE_CONTENTION) { throw new RecoverableException(Status.RESOURCE_CONTENTION); } throw e; } } @Override public void unloadSoundModel(int modelHandle) { try { mDriver.unloadSoundModel(modelHandle); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } @Override public void startRecognition(int modelHandle, int deviceHandle, int ioHandle, RecognitionConfig config) { try { mDriver.startRecognition(modelHandle, deviceHandle, ioHandle, config); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } catch (ServiceSpecificException e) { if (e.errorCode == Status.RESOURCE_CONTENTION) { throw new RecoverableException(Status.RESOURCE_CONTENTION); } throw e; } } @Override public void stopRecognition(int modelHandle) { try { mDriver.stopRecognition(modelHandle); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } @Override public void forceRecognitionEvent(int modelHandle) { try { mDriver.forceRecognitionEvent(modelHandle); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } @Override public ModelParameterRange queryParameter(int modelHandle, int param) { try { return mDriver.queryParameter(modelHandle, param); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } @Override public int getModelParameter(int modelHandle, int param) { try { return mDriver.getParameter(modelHandle, param); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } @Override public void setModelParameter(int modelHandle, int param, int value) { try { mDriver.setParameter(modelHandle, param, value); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } @Override public String interfaceDescriptor() { try { return mDriver.asBinder().getInterfaceDescriptor(); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } @Override public void linkToDeath(IBinder.DeathRecipient recipient) { try { mDriver.asBinder().linkToDeath(recipient, 0); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } @Override public void unlinkToDeath(IBinder.DeathRecipient recipient) { mDriver.asBinder().unlinkToDeath(recipient, 0); } @Override public void flushCallbacks() { // No-op. } @Override public void clientAttached(IBinder binder) { // No-op. This method is for test purposes, and is intercepted above. } @Override public void clientDetached(IBinder binder) { // No-op. This method is for test purposes, and is intercepted above. } @Override public void reboot() { mRebootRunnable.run(); } @Override public void detach() { // No-op. } private static class GlobalCallbackAdaper extends ISoundTriggerHwGlobalCallback.Stub { private final @NonNull GlobalCallback mDelegate; public GlobalCallbackAdaper(@NonNull GlobalCallback callback) { mDelegate = callback; } @Override public void onResourcesAvailable() { mDelegate.onResourcesAvailable(); } @Override public int getInterfaceVersion() { return ISoundTriggerHwGlobalCallback.VERSION; } @Override public String getInterfaceHash() { return ISoundTriggerHwGlobalCallback.HASH; } } private static class ModelCallbackAdaper extends ISoundTriggerHwCallback.Stub { private final @NonNull ModelCallback mDelegate; public ModelCallbackAdaper(ModelCallback callback) { mDelegate = callback; } @Override public void modelUnloaded(int model) { mDelegate.modelUnloaded(model); } @Override public void phraseRecognitionCallback(int model, PhraseRecognitionEvent event) { // A FORCED status implies that recognition is still active after the event. event.common.recognitionStillActive |= event.common.status == RecognitionStatus.FORCED; PhraseRecognitionEventSys phraseRecognitionEventSys = new PhraseRecognitionEventSys(); phraseRecognitionEventSys.phraseRecognitionEvent = event; phraseRecognitionEventSys.halEventReceivedMillis = SystemClock.elapsedRealtimeNanos(); mDelegate.phraseRecognitionCallback(model, phraseRecognitionEventSys); } @Override public void recognitionCallback(int model, RecognitionEvent event) { // A FORCED status implies that recognition is still active after the event. event.recognitionStillActive |= event.status == RecognitionStatus.FORCED; RecognitionEventSys recognitionEventSys = new RecognitionEventSys(); recognitionEventSys.recognitionEvent = event; recognitionEventSys.halEventReceivedMillis = SystemClock.elapsedRealtimeNanos(); mDelegate.recognitionCallback(model, recognitionEventSys); } @Override public int getInterfaceVersion() { return ISoundTriggerHwCallback.VERSION; } @Override public String getInterfaceHash() { return ISoundTriggerHwCallback.HASH; } } }