blob: 4b464d2d3e044a461f221d6d2a78acb7cfcefa6c [file] [log] [blame]
/*
* Copyright (C) 2019 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.content.Context;
import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
import android.media.soundtrigger_middleware.ModelParameterRange;
import android.media.soundtrigger_middleware.PhraseSoundModel;
import android.media.soundtrigger_middleware.RecognitionConfig;
import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.os.RemoteException;
import android.util.Log;
import com.android.server.SystemService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Objects;
/**
* This is a wrapper around an {@link ISoundTriggerMiddlewareService} implementation, which exposes
* it as a Binder service.
* <p>
* This is intended to facilitate a pattern of decorating the core implementation (business logic)
* of the interface with every decorator implementing a different aspect of the service, such as
* validation and logging. This class acts as the top-level decorator, which also adds the binder-
* related functionality (hence, it extends ISoundTriggerMiddlewareService.Stub as rather than
* implements ISoundTriggerMiddlewareService), and does the same thing for child interfaces
* returned.
* <p>
* The inner class {@link Lifecycle} acts as both a factory, composing the various aspect-decorators
* to create a full-featured implementation, as well as as an entry-point for presenting this
* implementation as a system service.
* <p>
* <b>Exposing this service as a System Service:</b><br>
* Insert this line into {@link com.android.server.SystemServer}:
* <code><pre>
* mSystemServiceManager.startService(SoundTriggerMiddlewareService.Lifecycle.class);
* </pre></code>
*
* {@hide}
*/
public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareService.Stub {
static private final String TAG = "SoundTriggerMiddlewareService";
@NonNull
private final ISoundTriggerMiddlewareInternal mDelegate;
/**
* Constructor for internal use only. Could be exposed for testing purposes in the future.
* Users should access this class via {@link Lifecycle}.
*/
private SoundTriggerMiddlewareService(@NonNull ISoundTriggerMiddlewareInternal delegate) {
mDelegate = Objects.requireNonNull(delegate);
new ExternalCaptureStateTracker(active -> {
try {
mDelegate.setCaptureState(active);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
});
}
@Override
public @NonNull
SoundTriggerModuleDescriptor[] listModules() throws RemoteException {
return mDelegate.listModules();
}
@Override
public @NonNull
ISoundTriggerModule attach(int handle, @NonNull ISoundTriggerCallback callback)
throws RemoteException {
return new ModuleService(mDelegate.attach(handle, callback));
}
@Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
if (mDelegate instanceof Dumpable) {
((Dumpable) mDelegate).dump(fout);
}
}
private final static class ModuleService extends ISoundTriggerModule.Stub {
private final ISoundTriggerModule mDelegate;
private ModuleService(ISoundTriggerModule delegate) {
mDelegate = delegate;
}
@Override
public int loadModel(SoundModel model) throws RemoteException {
return mDelegate.loadModel(model);
}
@Override
public int loadPhraseModel(PhraseSoundModel model) throws RemoteException {
return mDelegate.loadPhraseModel(model);
}
@Override
public void unloadModel(int modelHandle) throws RemoteException {
mDelegate.unloadModel(modelHandle);
}
@Override
public void startRecognition(int modelHandle, RecognitionConfig config)
throws RemoteException {
mDelegate.startRecognition(modelHandle, config);
}
@Override
public void stopRecognition(int modelHandle) throws RemoteException {
mDelegate.stopRecognition(modelHandle);
}
@Override
public void forceRecognitionEvent(int modelHandle) throws RemoteException {
mDelegate.forceRecognitionEvent(modelHandle);
}
@Override
public void setModelParameter(int modelHandle, int modelParam, int value)
throws RemoteException {
mDelegate.setModelParameter(modelHandle, modelParam, value);
}
@Override
public int getModelParameter(int modelHandle, int modelParam) throws RemoteException {
return mDelegate.getModelParameter(modelHandle, modelParam);
}
@Override
public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam)
throws RemoteException {
return mDelegate.queryModelParameterSupport(modelHandle, modelParam);
}
@Override
public void detach() throws RemoteException {
mDelegate.detach();
}
}
/**
* Entry-point to this module: exposes the module as a {@link SystemService}.
*/
public static final class Lifecycle extends SystemService {
public Lifecycle(Context context) {
super(context);
}
@Override
public void onStart() {
HalFactory[] factories = new HalFactory[]{() -> {
try {
Log.d(TAG, "Connecting to default ISoundTriggerHw");
return ISoundTriggerHw.getService(true);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}};
publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE,
new SoundTriggerMiddlewareService(
new SoundTriggerMiddlewareLogging(
new SoundTriggerMiddlewareValidation(
new SoundTriggerMiddlewareImpl(factories,
new AudioSessionProviderImpl()),
getContext()))));
}
}
}