1 /*
2  * Copyright (C) 2023 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.Nullable;
20 import android.media.soundtrigger.Phrase;
21 import android.media.soundtrigger.RecognitionConfig;
22 import android.media.soundtrigger.SoundModel;
23 import android.media.soundtrigger_middleware.IInjectGlobalEvent;
24 import android.media.soundtrigger_middleware.IInjectModelEvent;
25 import android.media.soundtrigger_middleware.IInjectRecognitionEvent;
26 import android.media.soundtrigger_middleware.ISoundTriggerInjection;
27 import android.os.IBinder;
28 import android.os.RemoteException;
29 import android.util.Slog;
30 
31 import com.android.internal.annotations.GuardedBy;
32 
33 import java.util.Objects;
34 
35 /**
36  * Service side of the injection interface which enforces a single client.
37  * Essentially a facade that presents an ever-present, single injection client to the fake STHAL.
38  * Proxies a binder interface, but should never be called as such.
39  * @hide
40  */
41 
42 public class SoundTriggerInjection implements ISoundTriggerInjection, IBinder.DeathRecipient {
43 
44     private static final String TAG = "SoundTriggerInjection";
45 
46     private final Object mClientLock = new Object();
47     @GuardedBy("mClientLock")
48     private ISoundTriggerInjection mClient = null;
49     @GuardedBy("mClientLock")
50     private IInjectGlobalEvent mGlobalEventInjection = null;
51 
52     /**
53      * Register a remote injection client.
54      * @param client - The injection client to register
55      */
registerClient(ISoundTriggerInjection client)56     public void registerClient(ISoundTriggerInjection client) {
57         synchronized (mClientLock) {
58             Objects.requireNonNull(client);
59             if (mClient != null) {
60                 try {
61                     mClient.onPreempted();
62                 } catch (RemoteException e) {
63                     Slog.e(TAG, "RemoteException when handling preemption", e);
64                 }
65                 mClient.asBinder().unlinkToDeath(this, 0);
66             }
67             mClient = client;
68             // Register cached global event injection interfaces,
69             // in case our client missed them.
70             try {
71                 mClient.asBinder().linkToDeath(this, 0);
72                 if (mGlobalEventInjection != null) {
73                     mClient.registerGlobalEventInjection(mGlobalEventInjection);
74                 }
75             } catch (RemoteException e) {
76                 mClient = null;
77             }
78 
79         }
80     }
81 
82     @Override
binderDied()83     public void binderDied() {
84         Slog.wtf(TAG, "Binder died without params");
85     }
86 
87     // If the binder has died, clear out mClient.
88     @Override
binderDied(IBinder who)89     public void binderDied(IBinder who) {
90         synchronized (mClientLock) {
91             if (mClient != null && who == mClient.asBinder()) {
92                 mClient = null;
93             }
94         }
95     }
96 
97     @Override
registerGlobalEventInjection(IInjectGlobalEvent globalInjection)98     public void registerGlobalEventInjection(IInjectGlobalEvent globalInjection) {
99         synchronized (mClientLock) {
100             // Cache for late attaching clients
101             mGlobalEventInjection = globalInjection;
102             if (mClient == null) return;
103             try {
104                 mClient.registerGlobalEventInjection(mGlobalEventInjection);
105             } catch (RemoteException e) {
106                 mClient = null;
107             }
108         }
109     }
110 
111     @Override
onRestarted(IInjectGlobalEvent globalSession)112     public void onRestarted(IInjectGlobalEvent globalSession) {
113         synchronized (mClientLock) {
114             if (mClient == null) return;
115             try {
116                 mClient.onRestarted(globalSession);
117             } catch (RemoteException e) {
118                 mClient = null;
119             }
120         }
121     }
122 
123     @Override
onFrameworkDetached(IInjectGlobalEvent globalSession)124     public void onFrameworkDetached(IInjectGlobalEvent globalSession) {
125         synchronized (mClientLock) {
126             if (mClient == null) return;
127             try {
128                 mClient.onFrameworkDetached(globalSession);
129             } catch (RemoteException e) {
130                 mClient = null;
131             }
132         }
133     }
134 
135     @Override
onClientAttached(IBinder token, IInjectGlobalEvent globalSession)136     public void onClientAttached(IBinder token, IInjectGlobalEvent globalSession) {
137         synchronized (mClientLock) {
138             if (mClient == null) return;
139             try {
140                 mClient.onClientAttached(token, globalSession);
141             } catch (RemoteException e) {
142                 mClient = null;
143             }
144         }
145     }
146 
147     @Override
onClientDetached(IBinder token)148     public void onClientDetached(IBinder token) {
149         synchronized (mClientLock) {
150             if (mClient == null) return;
151             try {
152                 mClient.onClientDetached(token);
153             } catch (RemoteException e) {
154                 mClient = null;
155             }
156         }
157     }
158 
159     @Override
onSoundModelLoaded(SoundModel model, @Nullable Phrase[] phrases, IInjectModelEvent modelInjection, IInjectGlobalEvent globalSession)160     public void onSoundModelLoaded(SoundModel model, @Nullable Phrase[] phrases,
161             IInjectModelEvent modelInjection, IInjectGlobalEvent globalSession) {
162         synchronized (mClientLock) {
163             if (mClient == null) return;
164             try {
165                 mClient.onSoundModelLoaded(model, phrases, modelInjection, globalSession);
166             } catch (RemoteException e) {
167                 mClient = null;
168             }
169         }
170     }
171 
172     @Override
onParamSet( int modelParam, int value, IInjectModelEvent modelSession)173     public void onParamSet(/** ModelParameter **/ int modelParam, int value,
174             IInjectModelEvent modelSession) {
175         synchronized (mClientLock) {
176             if (mClient == null) return;
177             try {
178                 mClient.onParamSet(modelParam, value, modelSession);
179             } catch (RemoteException e) {
180                 mClient = null;
181             }
182         }
183     }
184 
185     @Override
onRecognitionStarted(int audioSessionToken, RecognitionConfig config, IInjectRecognitionEvent recognitionInjection, IInjectModelEvent modelSession)186     public void onRecognitionStarted(int audioSessionToken, RecognitionConfig config,
187             IInjectRecognitionEvent recognitionInjection, IInjectModelEvent modelSession) {
188         synchronized (mClientLock) {
189             if (mClient == null) return;
190             try {
191                 mClient.onRecognitionStarted(audioSessionToken, config,
192                         recognitionInjection, modelSession);
193             } catch (RemoteException e) {
194                 mClient = null;
195             }
196         }
197     }
198 
199     @Override
onRecognitionStopped(IInjectRecognitionEvent recognitionSession)200     public void onRecognitionStopped(IInjectRecognitionEvent recognitionSession) {
201         synchronized (mClientLock) {
202             if (mClient == null) return;
203             try {
204                 mClient.onRecognitionStopped(recognitionSession);
205             } catch (RemoteException e) {
206                 mClient = null;
207             }
208         }
209     }
210 
211     @Override
onSoundModelUnloaded(IInjectModelEvent modelSession)212     public void onSoundModelUnloaded(IInjectModelEvent modelSession) {
213         synchronized (mClientLock) {
214             if (mClient == null) return;
215             try {
216                 mClient.onSoundModelUnloaded(modelSession);
217             } catch (RemoteException e) {
218                 mClient = null;
219             }
220         }
221     }
222 
223     @Override
onPreempted()224     public void onPreempted() {
225         // We are the service, so we can't be preempted.
226         Slog.wtf(TAG, "Unexpected preempted!");
227     }
228 
229     @Override
asBinder()230     public IBinder asBinder() {
231         // This class is not a real binder object
232         Slog.wtf(TAG, "Unexpected asBinder!");
233         throw new UnsupportedOperationException("Calling asBinder on a fake binder object");
234     }
235 
236 }
237