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 static android.Manifest.permission.SOUNDTRIGGER_DELEGATE_IDENTITY; 20 21 import android.annotation.NonNull; 22 import android.content.Context; 23 import android.content.PermissionChecker; 24 import android.media.permission.ClearCallingIdentityContext; 25 import android.media.permission.Identity; 26 import android.media.permission.PermissionUtil; 27 import android.media.permission.SafeCloseable; 28 import android.media.soundtrigger.ModelParameterRange; 29 import android.media.soundtrigger.PhraseSoundModel; 30 import android.media.soundtrigger.RecognitionConfig; 31 import android.media.soundtrigger.SoundModel; 32 import android.media.soundtrigger_middleware.ISoundTriggerCallback; 33 import android.media.soundtrigger_middleware.ISoundTriggerInjection; 34 import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService; 35 import android.media.soundtrigger_middleware.ISoundTriggerModule; 36 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor; 37 import android.os.IBinder; 38 import android.os.RemoteException; 39 40 import com.android.server.SystemService; 41 42 import java.io.FileDescriptor; 43 import java.io.PrintWriter; 44 import java.util.Objects; 45 46 /** 47 * This is a wrapper around an {@link ISoundTriggerMiddlewareService} implementation, which exposes 48 * it as a Binder service. 49 * <p> 50 * This is intended to facilitate a pattern of decorating the core implementation (business logic) 51 * of the interface with every decorator implementing a different aspect of the service, such as 52 * validation and logging. This class acts as the top-level decorator, which also adds the binder- 53 * related functionality (hence, it extends ISoundTriggerMiddlewareService.Stub as rather than 54 * implements ISoundTriggerMiddlewareService), and does the same thing for child interfaces 55 * returned. 56 * <p> 57 * The inner class {@link Lifecycle} acts as both a factory, composing the various aspect-decorators 58 * to create a full-featured implementation, as well as as an entry-point for presenting this 59 * implementation as a system service. 60 * <p> 61 * <b>Exposing this service as a System Service:</b><br> 62 * Insert this line into {@link com.android.server.SystemServer}: 63 * <code><pre> 64 * mSystemServiceManager.startService(SoundTriggerMiddlewareService.Lifecycle.class); 65 * </pre></code> 66 * 67 * {@hide} 68 */ 69 public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareService.Stub { 70 static private final String TAG = "SoundTriggerMiddlewareService"; 71 72 private final @NonNull ISoundTriggerMiddlewareInternal mDelegate; 73 private final @NonNull Context mContext; 74 // Lightweight object used to delegate injection events to the fake STHAL 75 private final @NonNull SoundTriggerInjection mInjection; 76 77 /** 78 * Constructor for internal use only. Could be exposed for testing purposes in the future. 79 * Users should access this class via {@link Lifecycle}. 80 */ SoundTriggerMiddlewareService(@onNull ISoundTriggerMiddlewareInternal delegate, @NonNull Context context, @NonNull SoundTriggerInjection injection)81 private SoundTriggerMiddlewareService(@NonNull ISoundTriggerMiddlewareInternal delegate, 82 @NonNull Context context, @NonNull SoundTriggerInjection injection) { 83 mDelegate = Objects.requireNonNull(delegate); 84 mContext = context; 85 mInjection = injection; 86 } 87 88 @Override listModulesAsOriginator(Identity identity)89 public SoundTriggerModuleDescriptor[] listModulesAsOriginator(Identity identity) { 90 try (SafeCloseable ignored = establishIdentityDirect(identity)) { 91 return mDelegate.listModules(); 92 } 93 } 94 95 @Override listModulesAsMiddleman(Identity middlemanIdentity, Identity originatorIdentity)96 public SoundTriggerModuleDescriptor[] listModulesAsMiddleman(Identity middlemanIdentity, 97 Identity originatorIdentity) { 98 try (SafeCloseable ignored = establishIdentityIndirect(middlemanIdentity, 99 originatorIdentity)) { 100 return mDelegate.listModules(); 101 } 102 } 103 104 @Override attachAsOriginator(int handle, Identity identity, ISoundTriggerCallback callback)105 public ISoundTriggerModule attachAsOriginator(int handle, Identity identity, 106 ISoundTriggerCallback callback) { 107 try (SafeCloseable ignored = establishIdentityDirect(Objects.requireNonNull(identity))) { 108 return new ModuleService(mDelegate.attach(handle, callback, /* isTrusted= */ false)); 109 } 110 } 111 112 @Override attachAsMiddleman(int handle, Identity middlemanIdentity, Identity originatorIdentity, ISoundTriggerCallback callback, boolean isTrusted)113 public ISoundTriggerModule attachAsMiddleman(int handle, Identity middlemanIdentity, 114 Identity originatorIdentity, ISoundTriggerCallback callback, boolean isTrusted) { 115 try (SafeCloseable ignored = establishIdentityIndirect( 116 Objects.requireNonNull(middlemanIdentity), 117 Objects.requireNonNull(originatorIdentity))) { 118 return new ModuleService(mDelegate.attach(handle, callback, isTrusted)); 119 } 120 } 121 122 @Override 123 @android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) attachFakeHalInjection(@onNull ISoundTriggerInjection injection)124 public void attachFakeHalInjection(@NonNull ISoundTriggerInjection injection) { 125 PermissionChecker.checkCallingOrSelfPermissionForPreflight( 126 mContext, android.Manifest.permission.MANAGE_SOUND_TRIGGER); 127 try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { 128 mInjection.registerClient(Objects.requireNonNull(injection)); 129 } 130 } 131 132 @Override dump(FileDescriptor fd, PrintWriter fout, String[] args)133 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 134 if (mDelegate instanceof Dumpable) { 135 ((Dumpable) mDelegate).dump(fout); 136 } 137 } 138 139 private @NonNull establishIdentityIndirect(Identity middlemanIdentity, Identity originatorIdentity)140 SafeCloseable establishIdentityIndirect(Identity middlemanIdentity, 141 Identity originatorIdentity) { 142 return PermissionUtil.establishIdentityIndirect(mContext, SOUNDTRIGGER_DELEGATE_IDENTITY, 143 middlemanIdentity, originatorIdentity); 144 } 145 146 private @NonNull establishIdentityDirect(Identity originatorIdentity)147 SafeCloseable establishIdentityDirect(Identity originatorIdentity) { 148 return PermissionUtil.establishIdentityDirect(originatorIdentity); 149 } 150 151 private final static class ModuleService extends ISoundTriggerModule.Stub { 152 private final ISoundTriggerModule mDelegate; 153 ModuleService(ISoundTriggerModule delegate)154 private ModuleService(ISoundTriggerModule delegate) { 155 mDelegate = delegate; 156 } 157 158 @Override loadModel(SoundModel model)159 public int loadModel(SoundModel model) throws RemoteException { 160 try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { 161 return mDelegate.loadModel(model); 162 } 163 } 164 165 @Override loadPhraseModel(PhraseSoundModel model)166 public int loadPhraseModel(PhraseSoundModel model) throws RemoteException { 167 try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { 168 return mDelegate.loadPhraseModel(model); 169 } 170 } 171 172 @Override unloadModel(int modelHandle)173 public void unloadModel(int modelHandle) throws RemoteException { 174 try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { 175 mDelegate.unloadModel(modelHandle); 176 } 177 } 178 179 @Override startRecognition(int modelHandle, RecognitionConfig config)180 public IBinder startRecognition(int modelHandle, RecognitionConfig config) 181 throws RemoteException { 182 try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { 183 return mDelegate.startRecognition(modelHandle, config); 184 } 185 } 186 187 @Override stopRecognition(int modelHandle)188 public void stopRecognition(int modelHandle) throws RemoteException { 189 try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { 190 mDelegate.stopRecognition(modelHandle); 191 } 192 } 193 194 @Override forceRecognitionEvent(int modelHandle)195 public void forceRecognitionEvent(int modelHandle) throws RemoteException { 196 try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { 197 mDelegate.forceRecognitionEvent(modelHandle); 198 } 199 } 200 201 @Override setModelParameter(int modelHandle, int modelParam, int value)202 public void setModelParameter(int modelHandle, int modelParam, int value) 203 throws RemoteException { 204 try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { 205 mDelegate.setModelParameter(modelHandle, modelParam, value); 206 } 207 } 208 209 @Override getModelParameter(int modelHandle, int modelParam)210 public int getModelParameter(int modelHandle, int modelParam) throws RemoteException { 211 try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { 212 return mDelegate.getModelParameter(modelHandle, modelParam); 213 } 214 } 215 216 @Override queryModelParameterSupport(int modelHandle, int modelParam)217 public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam) 218 throws RemoteException { 219 try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { 220 return mDelegate.queryModelParameterSupport(modelHandle, modelParam); 221 } 222 } 223 224 @Override detach()225 public void detach() throws RemoteException { 226 try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { 227 mDelegate.detach(); 228 } 229 } 230 } 231 232 /** 233 * Entry-point to this module: exposes the module as a {@link SystemService}. 234 */ 235 public static final class Lifecycle extends SystemService { Lifecycle(Context context)236 public Lifecycle(Context context) { 237 super(context); 238 } 239 240 @Override onStart()241 public void onStart() { 242 final SoundTriggerInjection injection = new SoundTriggerInjection(); 243 HalFactory[] factories = new HalFactory[]{new DefaultHalFactory(), 244 new FakeHalFactory(injection)}; 245 246 publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE, 247 new SoundTriggerMiddlewareService( 248 new SoundTriggerMiddlewareLogging(getContext(), 249 new SoundTriggerMiddlewarePermission( 250 new SoundTriggerMiddlewareValidation( 251 new SoundTriggerMiddlewareImpl(factories, 252 new AudioSessionProviderImpl())), 253 getContext())), getContext(), 254 injection)); 255 } 256 } 257 } 258