1 /*
2  * Copyright (C) 2020 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.os.IBinder;
26 import android.util.Slog;
27 
28 import java.util.Objects;
29 
30 /**
31  * An {@link ISoundTriggerHal} decorator that would enforce deadlines on all calls and reboot the
32  * HAL whenever they expire.
33  */
34 public class SoundTriggerHalWatchdog implements ISoundTriggerHal {
35     private static final long TIMEOUT_MS = 3000;
36     private static final String TAG = "SoundTriggerHalWatchdog";
37 
38     private final @NonNull ISoundTriggerHal mUnderlying;
39     private final @NonNull UptimeTimer mTimer;
40 
SoundTriggerHalWatchdog(@onNull ISoundTriggerHal underlying)41     public SoundTriggerHalWatchdog(@NonNull ISoundTriggerHal underlying) {
42         mUnderlying = Objects.requireNonNull(underlying);
43         mTimer = new UptimeTimer("SoundTriggerHalWatchdog");
44     }
45 
46     @Override
getProperties()47     public Properties getProperties() {
48         try (Watchdog ignore = new Watchdog()) {
49             return mUnderlying.getProperties();
50         }
51     }
52 
53     @Override
registerCallback(GlobalCallback callback)54     public void registerCallback(GlobalCallback callback) {
55         try (Watchdog ignore = new Watchdog()) {
56             mUnderlying.registerCallback(callback);
57         }
58     }
59 
60     @Override
loadSoundModel(SoundModel soundModel, ModelCallback callback)61     public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
62         try (Watchdog ignore = new Watchdog()) {
63             return mUnderlying.loadSoundModel(soundModel, callback);
64         }
65     }
66 
67     @Override
loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback)68     public int loadPhraseSoundModel(PhraseSoundModel soundModel,
69             ModelCallback callback) {
70         try (Watchdog ignore = new Watchdog()) {
71             return mUnderlying.loadPhraseSoundModel(soundModel, callback);
72         }
73     }
74 
75     @Override
unloadSoundModel(int modelHandle)76     public void unloadSoundModel(int modelHandle) {
77         try (Watchdog ignore = new Watchdog()) {
78             mUnderlying.unloadSoundModel(modelHandle);
79         }
80     }
81 
82     @Override
stopRecognition(int modelHandle)83     public void stopRecognition(int modelHandle) {
84         try (Watchdog ignore = new Watchdog()) {
85             mUnderlying.stopRecognition(modelHandle);
86         }
87     }
88 
89     @Override
startRecognition(int modelHandle, int deviceHandle, int ioHandle, RecognitionConfig config)90     public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
91             RecognitionConfig config) {
92         try (Watchdog ignore = new Watchdog()) {
93             mUnderlying.startRecognition(modelHandle, deviceHandle, ioHandle, config);
94         }
95     }
96 
97     @Override
forceRecognitionEvent(int modelHandle)98     public void forceRecognitionEvent(int modelHandle) {
99         try (Watchdog ignore = new Watchdog()) {
100             mUnderlying.forceRecognitionEvent(modelHandle);
101         }
102     }
103 
104     @Override
getModelParameter(int modelHandle, int param)105     public int getModelParameter(int modelHandle, int param) {
106         try (Watchdog ignore = new Watchdog()) {
107             return mUnderlying.getModelParameter(modelHandle, param);
108         }
109     }
110 
111     @Override
setModelParameter(int modelHandle, int param, int value)112     public void setModelParameter(int modelHandle, int param, int value) {
113         try (Watchdog ignore = new Watchdog()) {
114             mUnderlying.setModelParameter(modelHandle, param, value);
115         }
116     }
117 
118     @Override
queryParameter(int modelHandle, int param)119     public ModelParameterRange queryParameter(int modelHandle, int param) {
120         try (Watchdog ignore = new Watchdog()) {
121             return mUnderlying.queryParameter(modelHandle, param);
122         }
123     }
124 
125     @Override
linkToDeath(IBinder.DeathRecipient recipient)126     public void linkToDeath(IBinder.DeathRecipient recipient) {
127         mUnderlying.linkToDeath(recipient);
128     }
129 
130     @Override
unlinkToDeath(IBinder.DeathRecipient recipient)131     public void unlinkToDeath(IBinder.DeathRecipient recipient) {
132         mUnderlying.unlinkToDeath(recipient);
133     }
134 
135     @Override
interfaceDescriptor()136     public String interfaceDescriptor() {
137         return mUnderlying.interfaceDescriptor();
138     }
139 
140     @Override
flushCallbacks()141     public void flushCallbacks() {
142         mUnderlying.flushCallbacks();
143     }
144 
145     @Override
clientAttached(IBinder binder)146     public void clientAttached(IBinder binder) {
147         mUnderlying.clientAttached(binder);
148     }
149 
150     @Override
clientDetached(IBinder binder)151     public void clientDetached(IBinder binder) {
152         mUnderlying.clientDetached(binder);
153     }
154 
155     @Override
reboot()156     public void reboot() {
157         mUnderlying.reboot();
158     }
159 
160     @Override
detach()161     public void detach() {
162         mUnderlying.detach();
163         // We should no longer have any pending calls
164         mTimer.quit();
165     }
166 
167     private class Watchdog implements AutoCloseable {
168         private final @NonNull UptimeTimer.Task mTask;
169         // This exception is used merely for capturing a stack trace at the time of creation.
170         private final @NonNull
171         Exception mException = new Exception();
172 
Watchdog()173         Watchdog() {
174             mTask = mTimer.createTask(() -> {
175                 Slog.e(TAG, "HAL deadline expired. Rebooting.", mException);
176                 reboot();
177             }, TIMEOUT_MS);
178         }
179 
180         @Override
close()181         public void close() {
182             mTask.cancel();
183         }
184     }
185 }
186