blob: 8cdd2697db50b4d1df91b915b98664f749bdd91f [file] [log] [blame]
/*
* 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.media.soundtrigger.ModelParameterRange;
import android.media.soundtrigger.PhraseSoundModel;
import android.media.soundtrigger.Properties;
import android.media.soundtrigger.RecognitionConfig;
import android.media.soundtrigger.SoundModel;
import android.media.soundtrigger.Status;
import android.os.IBinder;
/**
* This is a decorator around ISoundTriggerHal, which implements enforcement of the maximum number
* of models supported by the HAL, for HAL implementations older than V2.4 that do not support
* rejection of model loading at the HAL layer.
* Since preemptive model unloading has been introduced in V2.4, it should never be used in
* conjunction with this class, hence we don't bother considering preemtive unloading when counting
* the number of currently loaded models.
*/
public class SoundTriggerHalMaxModelLimiter implements ISoundTriggerHal {
private final @NonNull ISoundTriggerHal mDelegate;
private final int mMaxModels;
// This counter is used to enforce the maximum number of loaded models.
private int mNumLoadedModels = 0;
private GlobalCallback mGlobalCallback;
public SoundTriggerHalMaxModelLimiter(
ISoundTriggerHal delegate, int maxModels) {
mDelegate = delegate;
this.mMaxModels = maxModels;
}
@Override
public void reboot() {
mDelegate.reboot();
}
@Override
public void detach() {
mDelegate.detach();
}
@Override
public Properties getProperties() {
return mDelegate.getProperties();
}
@Override
public void registerCallback(GlobalCallback callback) {
mGlobalCallback = callback;
mDelegate.registerCallback(mGlobalCallback);
}
@Override
public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
synchronized (this) {
if (mNumLoadedModels == mMaxModels) {
throw new RecoverableException(Status.RESOURCE_CONTENTION);
}
int result = mDelegate.loadSoundModel(soundModel, callback);
++mNumLoadedModels;
return result;
}
}
@Override
public int loadPhraseSoundModel(PhraseSoundModel soundModel,
ModelCallback callback) {
synchronized (this) {
if (mNumLoadedModels == mMaxModels) {
throw new RecoverableException(Status.RESOURCE_CONTENTION);
}
int result = mDelegate.loadPhraseSoundModel(soundModel, callback);
++mNumLoadedModels;
return result;
}
}
@Override
public void unloadSoundModel(int modelHandle) {
boolean wasAtMaxCapacity;
synchronized (this) {
wasAtMaxCapacity = mNumLoadedModels-- == mMaxModels;
}
try {
mDelegate.unloadSoundModel(modelHandle);
} catch (Exception e) {
synchronized (this) {
++mNumLoadedModels;
}
throw e;
}
if (wasAtMaxCapacity) {
// It is legal to invoke callbacks from within unloadSoundModel().
// See README.md for details.
mGlobalCallback.onResourcesAvailable();
}
}
@Override
public void stopRecognition(int modelHandle) {
mDelegate.stopRecognition(modelHandle);
}
@Override
public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
RecognitionConfig config) {
mDelegate.startRecognition(modelHandle, deviceHandle, ioHandle, config);
}
@Override
public void forceRecognitionEvent(int modelHandle) {
mDelegate.forceRecognitionEvent(modelHandle);
}
@Override
public int getModelParameter(int modelHandle, int param) {
return mDelegate.getModelParameter(modelHandle, param);
}
@Override
public void setModelParameter(int modelHandle, int param, int value) {
mDelegate.setModelParameter(modelHandle, param, value);
}
@Override
public ModelParameterRange queryParameter(int modelHandle, int param) {
return mDelegate.queryParameter(modelHandle, param);
}
@Override
public void linkToDeath(IBinder.DeathRecipient recipient) {
mDelegate.linkToDeath(recipient);
}
@Override
public void unlinkToDeath(IBinder.DeathRecipient recipient) {
mDelegate.unlinkToDeath(recipient);
}
@Override
public String interfaceDescriptor() {
return mDelegate.interfaceDescriptor();
}
@Override
public void flushCallbacks() {
mDelegate.flushCallbacks();
}
@Override
public void clientAttached(IBinder binder) {
mDelegate.clientAttached(binder);
}
@Override
public void clientDetached(IBinder binder) {
mDelegate.clientDetached(binder);
}
}