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.media.soundtrigger.ModelParameterRange; 21 import android.media.soundtrigger.PhraseSoundModel; 22 import android.media.soundtrigger.Properties; 23 import android.media.soundtrigger.RecognitionConfig; 24 import android.media.soundtrigger.SoundModel; 25 import android.media.soundtrigger.Status; 26 import android.os.IBinder; 27 28 /** 29 * This is a decorator around ISoundTriggerHal, which implements enforcement of the maximum number 30 * of models supported by the HAL, for HAL implementations older than V2.4 that do not support 31 * rejection of model loading at the HAL layer. 32 * Since preemptive model unloading has been introduced in V2.4, it should never be used in 33 * conjunction with this class, hence we don't bother considering preemtive unloading when counting 34 * the number of currently loaded models. 35 */ 36 public class SoundTriggerHalMaxModelLimiter implements ISoundTriggerHal { 37 private final @NonNull ISoundTriggerHal mDelegate; 38 private final int mMaxModels; 39 40 // This counter is used to enforce the maximum number of loaded models. 41 private int mNumLoadedModels = 0; 42 43 private GlobalCallback mGlobalCallback; 44 SoundTriggerHalMaxModelLimiter( ISoundTriggerHal delegate, int maxModels)45 public SoundTriggerHalMaxModelLimiter( 46 ISoundTriggerHal delegate, int maxModels) { 47 mDelegate = delegate; 48 this.mMaxModels = maxModels; 49 } 50 51 @Override reboot()52 public void reboot() { 53 mDelegate.reboot(); 54 } 55 56 @Override detach()57 public void detach() { 58 mDelegate.detach(); 59 } 60 61 @Override getProperties()62 public Properties getProperties() { 63 return mDelegate.getProperties(); 64 } 65 66 @Override registerCallback(GlobalCallback callback)67 public void registerCallback(GlobalCallback callback) { 68 mGlobalCallback = callback; 69 mDelegate.registerCallback(mGlobalCallback); 70 } 71 72 @Override loadSoundModel(SoundModel soundModel, ModelCallback callback)73 public int loadSoundModel(SoundModel soundModel, ModelCallback callback) { 74 synchronized (this) { 75 if (mNumLoadedModels == mMaxModels) { 76 throw new RecoverableException(Status.RESOURCE_CONTENTION); 77 } 78 int result = mDelegate.loadSoundModel(soundModel, callback); 79 ++mNumLoadedModels; 80 return result; 81 } 82 } 83 84 @Override loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback)85 public int loadPhraseSoundModel(PhraseSoundModel soundModel, 86 ModelCallback callback) { 87 synchronized (this) { 88 if (mNumLoadedModels == mMaxModels) { 89 throw new RecoverableException(Status.RESOURCE_CONTENTION); 90 } 91 int result = mDelegate.loadPhraseSoundModel(soundModel, callback); 92 ++mNumLoadedModels; 93 return result; 94 } 95 } 96 97 @Override unloadSoundModel(int modelHandle)98 public void unloadSoundModel(int modelHandle) { 99 boolean wasAtMaxCapacity; 100 synchronized (this) { 101 wasAtMaxCapacity = mNumLoadedModels-- == mMaxModels; 102 } 103 try { 104 mDelegate.unloadSoundModel(modelHandle); 105 } catch (Exception e) { 106 synchronized (this) { 107 ++mNumLoadedModels; 108 } 109 throw e; 110 } 111 if (wasAtMaxCapacity) { 112 // It is legal to invoke callbacks from within unloadSoundModel(). 113 // See README.md for details. 114 mGlobalCallback.onResourcesAvailable(); 115 } 116 } 117 118 @Override stopRecognition(int modelHandle)119 public void stopRecognition(int modelHandle) { 120 mDelegate.stopRecognition(modelHandle); 121 } 122 123 @Override startRecognition(int modelHandle, int deviceHandle, int ioHandle, RecognitionConfig config)124 public void startRecognition(int modelHandle, int deviceHandle, int ioHandle, 125 RecognitionConfig config) { 126 mDelegate.startRecognition(modelHandle, deviceHandle, ioHandle, config); 127 } 128 129 @Override forceRecognitionEvent(int modelHandle)130 public void forceRecognitionEvent(int modelHandle) { 131 mDelegate.forceRecognitionEvent(modelHandle); 132 } 133 134 @Override getModelParameter(int modelHandle, int param)135 public int getModelParameter(int modelHandle, int param) { 136 return mDelegate.getModelParameter(modelHandle, param); 137 } 138 139 @Override setModelParameter(int modelHandle, int param, int value)140 public void setModelParameter(int modelHandle, int param, int value) { 141 mDelegate.setModelParameter(modelHandle, param, value); 142 } 143 144 @Override queryParameter(int modelHandle, int param)145 public ModelParameterRange queryParameter(int modelHandle, int param) { 146 return mDelegate.queryParameter(modelHandle, param); 147 } 148 149 @Override linkToDeath(IBinder.DeathRecipient recipient)150 public void linkToDeath(IBinder.DeathRecipient recipient) { 151 mDelegate.linkToDeath(recipient); 152 } 153 154 @Override unlinkToDeath(IBinder.DeathRecipient recipient)155 public void unlinkToDeath(IBinder.DeathRecipient recipient) { 156 mDelegate.unlinkToDeath(recipient); 157 } 158 159 @Override interfaceDescriptor()160 public String interfaceDescriptor() { 161 return mDelegate.interfaceDescriptor(); 162 } 163 164 @Override flushCallbacks()165 public void flushCallbacks() { 166 mDelegate.flushCallbacks(); 167 } 168 169 @Override clientAttached(IBinder binder)170 public void clientAttached(IBinder binder) { 171 mDelegate.clientAttached(binder); 172 } 173 174 @Override clientDetached(IBinder binder)175 public void clientDetached(IBinder binder) { 176 mDelegate.clientDetached(binder); 177 } 178 } 179