Home | History | Annotate | Download | only in soundtrigger
      1 /*
      2  * Copyright (C) 2014 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;
     18 import static android.hardware.soundtrigger.SoundTrigger.STATUS_ERROR;
     19 
     20 import android.content.Context;
     21 import android.content.pm.PackageManager;
     22 import android.Manifest;
     23 import android.hardware.soundtrigger.IRecognitionStatusCallback;
     24 import android.hardware.soundtrigger.SoundTrigger;
     25 import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
     26 import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
     27 import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
     28 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
     29 import android.os.Parcel;
     30 import android.os.ParcelUuid;
     31 import android.os.RemoteException;
     32 import android.util.Slog;
     33 
     34 import com.android.server.SystemService;
     35 import com.android.internal.app.ISoundTriggerService;
     36 
     37 import java.io.FileDescriptor;
     38 import java.io.PrintWriter;
     39 import java.util.UUID;
     40 
     41 /**
     42  * A single SystemService to manage all sound/voice-based sound models on the DSP.
     43  * This services provides apis to manage sound trigger-based sound models via
     44  * the ISoundTriggerService interface. This class also publishes a local interface encapsulating
     45  * the functionality provided by {@link SoundTriggerHelper} for use by
     46  * {@link VoiceInteractionManagerService}.
     47  *
     48  * @hide
     49  */
     50 public class SoundTriggerService extends SystemService {
     51     private static final String TAG = "SoundTriggerService";
     52     private static final boolean DEBUG = true;
     53 
     54     final Context mContext;
     55     private final SoundTriggerServiceStub mServiceStub;
     56     private final LocalSoundTriggerService mLocalSoundTriggerService;
     57     private SoundTriggerDbHelper mDbHelper;
     58     private SoundTriggerHelper mSoundTriggerHelper;
     59 
     60     public SoundTriggerService(Context context) {
     61         super(context);
     62         mContext = context;
     63         mServiceStub = new SoundTriggerServiceStub();
     64         mLocalSoundTriggerService = new LocalSoundTriggerService(context);
     65     }
     66 
     67     @Override
     68     public void onStart() {
     69         publishBinderService(Context.SOUND_TRIGGER_SERVICE, mServiceStub);
     70         publishLocalService(SoundTriggerInternal.class, mLocalSoundTriggerService);
     71     }
     72 
     73     @Override
     74     public void onBootPhase(int phase) {
     75         if (PHASE_SYSTEM_SERVICES_READY == phase) {
     76             initSoundTriggerHelper();
     77             mLocalSoundTriggerService.setSoundTriggerHelper(mSoundTriggerHelper);
     78         } else if (PHASE_THIRD_PARTY_APPS_CAN_START == phase) {
     79             mDbHelper = new SoundTriggerDbHelper(mContext);
     80         }
     81     }
     82 
     83     @Override
     84     public void onStartUser(int userHandle) {
     85     }
     86 
     87     @Override
     88     public void onSwitchUser(int userHandle) {
     89     }
     90 
     91     private synchronized void initSoundTriggerHelper() {
     92         if (mSoundTriggerHelper == null) {
     93             mSoundTriggerHelper = new SoundTriggerHelper(mContext);
     94         }
     95     }
     96 
     97     private synchronized boolean isInitialized() {
     98         if (mSoundTriggerHelper == null ) {
     99             Slog.e(TAG, "SoundTriggerHelper not initialized.");
    100             return false;
    101         }
    102         return true;
    103     }
    104 
    105     class SoundTriggerServiceStub extends ISoundTriggerService.Stub {
    106         @Override
    107         public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
    108                 throws RemoteException {
    109             try {
    110                 return super.onTransact(code, data, reply, flags);
    111             } catch (RuntimeException e) {
    112                 // The activity manager only throws security exceptions, so let's
    113                 // log all others.
    114                 if (!(e instanceof SecurityException)) {
    115                     Slog.wtf(TAG, "SoundTriggerService Crash", e);
    116                 }
    117                 throw e;
    118             }
    119         }
    120 
    121         @Override
    122         public int startRecognition(ParcelUuid parcelUuid, IRecognitionStatusCallback callback,
    123                 RecognitionConfig config) {
    124             enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
    125             if (!isInitialized()) return STATUS_ERROR;
    126             if (DEBUG) {
    127                 Slog.i(TAG, "startRecognition(): Uuid : " + parcelUuid);
    128             }
    129 
    130             GenericSoundModel model = getSoundModel(parcelUuid);
    131             if (model == null) {
    132                 Slog.e(TAG, "Null model in database for id: " + parcelUuid);
    133                 return STATUS_ERROR;
    134             }
    135 
    136             return mSoundTriggerHelper.startGenericRecognition(parcelUuid.getUuid(), model,
    137                     callback, config);
    138         }
    139 
    140         @Override
    141         public int stopRecognition(ParcelUuid parcelUuid, IRecognitionStatusCallback callback) {
    142             enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
    143             if (DEBUG) {
    144                 Slog.i(TAG, "stopRecognition(): Uuid : " + parcelUuid);
    145             }
    146             if (!isInitialized()) return STATUS_ERROR;
    147             return mSoundTriggerHelper.stopGenericRecognition(parcelUuid.getUuid(), callback);
    148         }
    149 
    150         @Override
    151         public SoundTrigger.GenericSoundModel getSoundModel(ParcelUuid soundModelId) {
    152             enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
    153             if (DEBUG) {
    154                 Slog.i(TAG, "getSoundModel(): id = " + soundModelId);
    155             }
    156             SoundTrigger.GenericSoundModel model = mDbHelper.getGenericSoundModel(
    157                     soundModelId.getUuid());
    158             return model;
    159         }
    160 
    161         @Override
    162         public void updateSoundModel(SoundTrigger.GenericSoundModel soundModel) {
    163             enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
    164             if (DEBUG) {
    165                 Slog.i(TAG, "updateSoundModel(): model = " + soundModel);
    166             }
    167             mDbHelper.updateGenericSoundModel(soundModel);
    168         }
    169 
    170         @Override
    171         public void deleteSoundModel(ParcelUuid soundModelId) {
    172             enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
    173             if (DEBUG) {
    174                 Slog.i(TAG, "deleteSoundModel(): id = " + soundModelId);
    175             }
    176             // Unload the model if it is loaded.
    177             mSoundTriggerHelper.unloadGenericSoundModel(soundModelId.getUuid());
    178             mDbHelper.deleteGenericSoundModel(soundModelId.getUuid());
    179         }
    180     }
    181 
    182     public final class LocalSoundTriggerService extends SoundTriggerInternal {
    183         private final Context mContext;
    184         private SoundTriggerHelper mSoundTriggerHelper;
    185 
    186         LocalSoundTriggerService(Context context) {
    187             mContext = context;
    188         }
    189 
    190         synchronized void setSoundTriggerHelper(SoundTriggerHelper helper) {
    191             mSoundTriggerHelper = helper;
    192         }
    193 
    194         @Override
    195         public int startRecognition(int keyphraseId, KeyphraseSoundModel soundModel,
    196                 IRecognitionStatusCallback listener, RecognitionConfig recognitionConfig) {
    197             if (!isInitialized()) return STATUS_ERROR;
    198             return mSoundTriggerHelper.startKeyphraseRecognition(keyphraseId, soundModel, listener,
    199                     recognitionConfig);
    200         }
    201 
    202         @Override
    203         public synchronized int stopRecognition(int keyphraseId, IRecognitionStatusCallback listener) {
    204             if (!isInitialized()) return STATUS_ERROR;
    205             return mSoundTriggerHelper.stopKeyphraseRecognition(keyphraseId, listener);
    206         }
    207 
    208         @Override
    209         public ModuleProperties getModuleProperties() {
    210             if (!isInitialized()) return null;
    211             return mSoundTriggerHelper.getModuleProperties();
    212         }
    213 
    214         @Override
    215         public int unloadKeyphraseModel(int keyphraseId) {
    216             if (!isInitialized()) return STATUS_ERROR;
    217             return mSoundTriggerHelper.unloadKeyphraseSoundModel(keyphraseId);
    218         }
    219 
    220         @Override
    221         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    222             if (!isInitialized()) return;
    223             mSoundTriggerHelper.dump(fd, pw, args);
    224         }
    225 
    226         private synchronized boolean isInitialized() {
    227             if (mSoundTriggerHelper == null ) {
    228                 Slog.e(TAG, "SoundTriggerHelper not initialized.");
    229                 return false;
    230             }
    231             return true;
    232         }
    233     }
    234 
    235     private void enforceCallingPermission(String permission) {
    236         if (mContext.checkCallingOrSelfPermission(permission)
    237                 != PackageManager.PERMISSION_GRANTED) {
    238             throw new SecurityException("Caller does not hold the permission " + permission);
    239         }
    240     }
    241 }
    242