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