1 /*
2  * Copyright (C) 2019 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_middleware.ISoundTriggerCallback;
21 import android.media.soundtrigger_middleware.ISoundTriggerModule;
22 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
23 import android.util.Slog;
24 
25 import java.util.ArrayList;
26 import java.util.List;
27 
28 /**
29  * This is an implementation of the ISoundTriggerMiddlewareService interface.
30  * <p>
31  * <b>Important conventions:</b>
32  * <ul>
33  * <li>Correct usage is assumed. This implementation does not attempt to gracefully handle invalid
34  * usage, and such usage will result in undefined behavior. If this service is to be offered to an
35  * untrusted client, it must be wrapped with input and state validation.
36  * <li>There is no binder instance associated with this implementation. Do not call asBinder().
37  * <li>The implementation may throw a {@link RecoverableException} to indicate non-fatal,
38  * recoverable faults. The error code would one of the
39  * {@link android.media.soundtrigger.Status}
40  * constants. Any other exception thrown should be regarded as a bug in the implementation or one
41  * of its dependencies (assuming correct usage).
42  * <li>The implementation is designed for testibility by featuring dependency injection (the
43  * underlying HAL driver instances are passed to the ctor) and by minimizing dependencies on
44  * Android runtime.
45  * <li>The implementation is thread-safe.
46  * </ul>
47  *
48  * @hide
49  */
50 public class SoundTriggerMiddlewareImpl implements ISoundTriggerMiddlewareInternal {
51     static private final String TAG = "SoundTriggerMiddlewareImpl";
52     private final SoundTriggerModule[] mModules;
53 
54     /**
55      * Interface to the audio system, which can allocate capture session handles.
56      * SoundTrigger uses those sessions in order to associate a recognition session with an optional
57      * capture from the same device that triggered the recognition.
58      */
59     public static abstract class AudioSessionProvider {
60         public static final class AudioSession {
61             final int mSessionHandle;
62             final int mIoHandle;
63             final int mDeviceHandle;
64 
AudioSession(int sessionHandle, int ioHandle, int deviceHandle)65             AudioSession(int sessionHandle, int ioHandle, int deviceHandle) {
66                 mSessionHandle = sessionHandle;
67                 mIoHandle = ioHandle;
68                 mDeviceHandle = deviceHandle;
69             }
70         }
71 
acquireSession()72         public abstract AudioSession acquireSession();
73 
releaseSession(int sessionHandle)74         public abstract void releaseSession(int sessionHandle);
75     }
76 
77     /**
78      * Constructor - gets an array of HAL driver factories.
79      */
SoundTriggerMiddlewareImpl(@onNull HalFactory[] halFactories, @NonNull AudioSessionProvider audioSessionProvider)80     public SoundTriggerMiddlewareImpl(@NonNull HalFactory[] halFactories,
81             @NonNull AudioSessionProvider audioSessionProvider) {
82         List<SoundTriggerModule> modules = new ArrayList<>(halFactories.length);
83 
84         for (HalFactory halFactory : halFactories) {
85             try {
86                 modules.add(new SoundTriggerModule(halFactory, audioSessionProvider));
87             } catch (Exception e) {
88                 Slog.e(TAG, "Failed to add a SoundTriggerModule instance", e);
89             }
90         }
91 
92         mModules = modules.toArray(new SoundTriggerModule[0]);
93     }
94 
95     /**
96      * Convenience constructor - gets a single HAL factory.
97      */
SoundTriggerMiddlewareImpl(@onNull HalFactory factory, @NonNull AudioSessionProvider audioSessionProvider)98     public SoundTriggerMiddlewareImpl(@NonNull HalFactory factory,
99             @NonNull AudioSessionProvider audioSessionProvider) {
100         this(new HalFactory[]{factory}, audioSessionProvider);
101     }
102 
103     @Override
104     public @NonNull
listModules()105     SoundTriggerModuleDescriptor[] listModules() {
106         SoundTriggerModuleDescriptor[] result = new SoundTriggerModuleDescriptor[mModules.length];
107 
108         for (int i = 0; i < mModules.length; ++i) {
109             SoundTriggerModuleDescriptor desc = new SoundTriggerModuleDescriptor();
110             desc.handle = i;
111             desc.properties = mModules[i].getProperties();
112             result[i] = desc;
113         }
114         return result;
115     }
116 
117     @Override
118     public @NonNull
attach(int handle, @NonNull ISoundTriggerCallback callback, boolean isTrusted)119     ISoundTriggerModule attach(int handle, @NonNull ISoundTriggerCallback callback,
120             boolean isTrusted) {
121         return mModules[handle].attach(callback);
122     }
123 }
124