Loading services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java +26 −4 Original line number Diff line number Diff line Loading @@ -119,6 +119,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { @GuardedBy("mLock") private SoundTriggerDeviceState mDeviceState = SoundTriggerDeviceState.DISABLE; @GuardedBy("mLock") private boolean mIsAppOpPermitted = true; SoundTriggerHelper(Context context, EventLogger eventLogger, @NonNull Function<SoundTrigger.StatusListener, SoundTriggerModule> moduleProvider, int moduleId, Loading Loading @@ -323,7 +326,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { modelData.setRunInBatterySaverMode(runInBatterySaverMode); modelData.setSoundModel(soundModel); if (isRecognitionAllowedByDeviceState(modelData)) { if (isRecognitionAllowed(modelData)) { int startRecoResult = updateRecognitionLocked(modelData, false /* Don't notify for synchronous calls */); if (startRecoResult == SoundTrigger.STATUS_OK) { Loading Loading @@ -613,6 +616,16 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } public void onAppOpStateChanged(boolean isPermitted) { synchronized (mLock) { if (mIsAppOpPermitted == isPermitted) { return; } mIsAppOpPermitted = isPermitted; updateAllRecognitionsLocked(); } } public int getGenericModelState(UUID modelId) { synchronized (mLock) { MetricsLogger.count(mContext, "sth_get_generic_model_state", 1); Loading Loading @@ -782,6 +795,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return event instanceof KeyphraseRecognitionEvent; } @GuardedBy("mLock") private void onGenericRecognitionLocked(GenericRecognitionEvent event) { MetricsLogger.count(mContext, "sth_generic_recognition_event", 1); if (event.status != SoundTrigger.RECOGNITION_STATUS_SUCCESS Loading Loading @@ -866,6 +880,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } @GuardedBy("mLock") private void onResourcesAvailableLocked() { mEventLogger.enqueue(new SessionEvent(Type.RESOURCES_AVAILABLE, null)); updateAllRecognitionsLocked(); Loading Loading @@ -911,6 +926,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return keyphraseExtras[0].id; } @GuardedBy("mLock") private void onKeyphraseRecognitionLocked(KeyphraseRecognitionEvent event) { Slog.i(TAG, "Recognition success"); MetricsLogger.count(mContext, "sth_keyphrase_recognition_event", 1); Loading Loading @@ -956,6 +972,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } @GuardedBy("mLock") private void updateAllRecognitionsLocked() { // updateRecognitionLocked can possibly update the list of models ArrayList<ModelData> modelDatas = new ArrayList<ModelData>(mModelDataMap.values()); Loading @@ -964,8 +981,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } @GuardedBy("mLock") private int updateRecognitionLocked(ModelData model, boolean notifyClientOnError) { boolean shouldStartModel = model.isRequested() && isRecognitionAllowedByDeviceState(model); boolean shouldStartModel = model.isRequested() && isRecognitionAllowed(model); if (shouldStartModel == model.isModelStarted()) { // No-op. return STATUS_OK; Loading Loading @@ -1184,7 +1202,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { * @return True if recognition is allowed to run at this time. False if not. */ @GuardedBy("mLock") private boolean isRecognitionAllowedByDeviceState(ModelData modelData) { private boolean isRecognitionAllowed(ModelData modelData) { if (!mIsAppOpPermitted) { return false; } return switch (mDeviceState) { case DISABLE -> false; case CRITICAL -> modelData.shouldRunInBatterySaverMode(); Loading @@ -1195,6 +1216,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { // A single routine that implements the start recognition logic for both generic and keyphrase // models. @GuardedBy("mLock") private int startRecognitionLocked(ModelData modelData, boolean notifyClientOnError) { IRecognitionStatusCallback callback = modelData.getCallback(); RecognitionConfig config = modelData.getRecognitionConfig(); Loading @@ -1205,7 +1227,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return STATUS_ERROR; } if (!isRecognitionAllowedByDeviceState(modelData)) { if (!isRecognitionAllowed(modelData)) { // Nothing to do here. Slog.w(TAG, "startRecognition requested but not allowed."); MetricsLogger.count(mContext, "sth_start_recognition_not_allowed", 1); Loading services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +65 −2 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityThread; import android.app.AppOpsManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; Loading Loading @@ -111,6 +112,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.function.Consumer; import java.util.List; import java.util.Set; import java.util.Deque; Loading Loading @@ -236,6 +238,8 @@ public class SoundTriggerService extends SystemService { private final DeviceStateHandler mDeviceStateHandler; private final Executor mDeviceStateHandlerExecutor = Executors.newSingleThreadExecutor(); private PhoneCallStateHandler mPhoneCallStateHandler; private AppOpsManager mAppOpsManager; private PackageManager mPackageManager; public SoundTriggerService(Context context) { super(context); Loading @@ -258,6 +262,8 @@ public class SoundTriggerService extends SystemService { Slog.d(TAG, "onBootPhase: " + phase + " : " + isSafeMode()); if (PHASE_THIRD_PARTY_APPS_CAN_START == phase) { mDbHelper = new SoundTriggerDbHelper(mContext); mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mPackageManager = mContext.getPackageManager(); final PowerManager powerManager = mContext.getSystemService(PowerManager.class); // Hook up power state listener mContext.registerReceiver( Loading Loading @@ -349,6 +355,44 @@ public class SoundTriggerService extends SystemService { } } class MyAppOpsListener implements AppOpsManager.OnOpChangedListener { private final Identity mOriginatorIdentity; private final Consumer<Boolean> mOnOpModeChanged; MyAppOpsListener(Identity originatorIdentity, Consumer<Boolean> onOpModeChanged) { mOriginatorIdentity = Objects.requireNonNull(originatorIdentity); mOnOpModeChanged = Objects.requireNonNull(onOpModeChanged); // Validate package name try { int uid = mPackageManager.getPackageUid(mOriginatorIdentity.packageName, PackageManager.PackageInfoFlags.of(0)); if (uid != mOriginatorIdentity.uid) { throw new SecurityException("Package name: " + mOriginatorIdentity.packageName + "with uid: " + uid + "attempted to spoof as: " + mOriginatorIdentity.uid); } } catch (PackageManager.NameNotFoundException e) { throw new SecurityException("Package name not found: " + mOriginatorIdentity.packageName); } } @Override public void onOpChanged(String op, String packageName) { if (!Objects.equals(op, AppOpsManager.OPSTR_RECORD_AUDIO)) { return; } final int mode = mAppOpsManager.checkOpNoThrow( AppOpsManager.OPSTR_RECORD_AUDIO, mOriginatorIdentity.uid, mOriginatorIdentity.packageName); mOnOpModeChanged.accept(mode == AppOpsManager.MODE_ALLOWED); } void forceOpChangeRefresh() { onOpChanged(AppOpsManager.OPSTR_RECORD_AUDIO, mOriginatorIdentity.packageName); } } class SoundTriggerServiceStub extends ISoundTriggerService.Stub { @Override public ISoundTriggerSession attachAsOriginator(@NonNull Identity originatorIdentity, Loading Loading @@ -463,6 +507,7 @@ public class SoundTriggerService extends SystemService { private final Object mCallbacksLock = new Object(); private final TreeMap<UUID, IRecognitionStatusCallback> mCallbacks = new TreeMap<>(); private final EventLogger mEventLogger; private final MyAppOpsListener mAppOpsListener; SoundTriggerSessionStub(@NonNull IBinder client, SoundTriggerHelper soundTriggerHelper, EventLogger eventLogger) { Loading @@ -479,6 +524,12 @@ public class SoundTriggerService extends SystemService { } mListener = (SoundTriggerDeviceState state) -> mSoundTriggerHelper.onDeviceStateChanged(state); mAppOpsListener = new MyAppOpsListener(mOriginatorIdentity, mSoundTriggerHelper::onAppOpStateChanged); mAppOpsListener.forceOpChangeRefresh(); mAppOpsManager.startWatchingMode(AppOpsManager.OPSTR_RECORD_AUDIO, mOriginatorIdentity.packageName, AppOpsManager.WATCH_FOREGROUND_CHANGES, mAppOpsListener); mDeviceStateHandler.registerListener(mListener); } Loading Loading @@ -930,6 +981,9 @@ public class SoundTriggerService extends SystemService { } private void detach() { if (mAppOpsListener != null) { mAppOpsManager.stopWatchingMode(mAppOpsListener); } mDeviceStateHandler.unregisterListener(mListener); mSoundTriggerHelper.detach(); detachSessionLogger(mEventLogger); Loading @@ -945,9 +999,8 @@ public class SoundTriggerService extends SystemService { } private void enforceDetectionPermissions(ComponentName detectionService) { PackageManager packageManager = mContext.getPackageManager(); String packageName = detectionService.getPackageName(); if (packageManager.checkPermission( if (mPackageManager.checkPermission( Manifest.permission.CAPTURE_AUDIO_HOTWORD, packageName) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException(detectionService.getPackageName() + " does not have" Loading Loading @@ -1635,6 +1688,7 @@ public class SoundTriggerService extends SystemService { private final EventLogger mEventLogger; private final Identity mOriginatorIdentity; private final @NonNull DeviceStateListener mListener; private final MyAppOpsListener mAppOpsListener; private final SparseArray<UUID> mModelUuid = new SparseArray<>(1); Loading @@ -1655,6 +1709,12 @@ public class SoundTriggerService extends SystemService { } mListener = (SoundTriggerDeviceState state) -> mSoundTriggerHelper.onDeviceStateChanged(state); mAppOpsListener = new MyAppOpsListener(mOriginatorIdentity, mSoundTriggerHelper::onAppOpStateChanged); mAppOpsListener.forceOpChangeRefresh(); mAppOpsManager.startWatchingMode(AppOpsManager.OPSTR_RECORD_AUDIO, mOriginatorIdentity.packageName, AppOpsManager.WATCH_FOREGROUND_CHANGES, mAppOpsListener); mDeviceStateHandler.registerListener(mListener); } Loading Loading @@ -1722,6 +1782,9 @@ public class SoundTriggerService extends SystemService { } private void detachInternal() { if (mAppOpsListener != null) { mAppOpsManager.stopWatchingMode(mAppOpsListener); } mEventLogger.enqueue(new SessionEvent(Type.DETACH, null)); detachSessionLogger(mEventLogger); mDeviceStateHandler.unregisterListener(mListener); Loading Loading
services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java +26 −4 Original line number Diff line number Diff line Loading @@ -119,6 +119,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { @GuardedBy("mLock") private SoundTriggerDeviceState mDeviceState = SoundTriggerDeviceState.DISABLE; @GuardedBy("mLock") private boolean mIsAppOpPermitted = true; SoundTriggerHelper(Context context, EventLogger eventLogger, @NonNull Function<SoundTrigger.StatusListener, SoundTriggerModule> moduleProvider, int moduleId, Loading Loading @@ -323,7 +326,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { modelData.setRunInBatterySaverMode(runInBatterySaverMode); modelData.setSoundModel(soundModel); if (isRecognitionAllowedByDeviceState(modelData)) { if (isRecognitionAllowed(modelData)) { int startRecoResult = updateRecognitionLocked(modelData, false /* Don't notify for synchronous calls */); if (startRecoResult == SoundTrigger.STATUS_OK) { Loading Loading @@ -613,6 +616,16 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } public void onAppOpStateChanged(boolean isPermitted) { synchronized (mLock) { if (mIsAppOpPermitted == isPermitted) { return; } mIsAppOpPermitted = isPermitted; updateAllRecognitionsLocked(); } } public int getGenericModelState(UUID modelId) { synchronized (mLock) { MetricsLogger.count(mContext, "sth_get_generic_model_state", 1); Loading Loading @@ -782,6 +795,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return event instanceof KeyphraseRecognitionEvent; } @GuardedBy("mLock") private void onGenericRecognitionLocked(GenericRecognitionEvent event) { MetricsLogger.count(mContext, "sth_generic_recognition_event", 1); if (event.status != SoundTrigger.RECOGNITION_STATUS_SUCCESS Loading Loading @@ -866,6 +880,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } @GuardedBy("mLock") private void onResourcesAvailableLocked() { mEventLogger.enqueue(new SessionEvent(Type.RESOURCES_AVAILABLE, null)); updateAllRecognitionsLocked(); Loading Loading @@ -911,6 +926,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return keyphraseExtras[0].id; } @GuardedBy("mLock") private void onKeyphraseRecognitionLocked(KeyphraseRecognitionEvent event) { Slog.i(TAG, "Recognition success"); MetricsLogger.count(mContext, "sth_keyphrase_recognition_event", 1); Loading Loading @@ -956,6 +972,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } @GuardedBy("mLock") private void updateAllRecognitionsLocked() { // updateRecognitionLocked can possibly update the list of models ArrayList<ModelData> modelDatas = new ArrayList<ModelData>(mModelDataMap.values()); Loading @@ -964,8 +981,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } @GuardedBy("mLock") private int updateRecognitionLocked(ModelData model, boolean notifyClientOnError) { boolean shouldStartModel = model.isRequested() && isRecognitionAllowedByDeviceState(model); boolean shouldStartModel = model.isRequested() && isRecognitionAllowed(model); if (shouldStartModel == model.isModelStarted()) { // No-op. return STATUS_OK; Loading Loading @@ -1184,7 +1202,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { * @return True if recognition is allowed to run at this time. False if not. */ @GuardedBy("mLock") private boolean isRecognitionAllowedByDeviceState(ModelData modelData) { private boolean isRecognitionAllowed(ModelData modelData) { if (!mIsAppOpPermitted) { return false; } return switch (mDeviceState) { case DISABLE -> false; case CRITICAL -> modelData.shouldRunInBatterySaverMode(); Loading @@ -1195,6 +1216,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { // A single routine that implements the start recognition logic for both generic and keyphrase // models. @GuardedBy("mLock") private int startRecognitionLocked(ModelData modelData, boolean notifyClientOnError) { IRecognitionStatusCallback callback = modelData.getCallback(); RecognitionConfig config = modelData.getRecognitionConfig(); Loading @@ -1205,7 +1227,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return STATUS_ERROR; } if (!isRecognitionAllowedByDeviceState(modelData)) { if (!isRecognitionAllowed(modelData)) { // Nothing to do here. Slog.w(TAG, "startRecognition requested but not allowed."); MetricsLogger.count(mContext, "sth_start_recognition_not_allowed", 1); Loading
services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +65 −2 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityThread; import android.app.AppOpsManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; Loading Loading @@ -111,6 +112,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.function.Consumer; import java.util.List; import java.util.Set; import java.util.Deque; Loading Loading @@ -236,6 +238,8 @@ public class SoundTriggerService extends SystemService { private final DeviceStateHandler mDeviceStateHandler; private final Executor mDeviceStateHandlerExecutor = Executors.newSingleThreadExecutor(); private PhoneCallStateHandler mPhoneCallStateHandler; private AppOpsManager mAppOpsManager; private PackageManager mPackageManager; public SoundTriggerService(Context context) { super(context); Loading @@ -258,6 +262,8 @@ public class SoundTriggerService extends SystemService { Slog.d(TAG, "onBootPhase: " + phase + " : " + isSafeMode()); if (PHASE_THIRD_PARTY_APPS_CAN_START == phase) { mDbHelper = new SoundTriggerDbHelper(mContext); mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mPackageManager = mContext.getPackageManager(); final PowerManager powerManager = mContext.getSystemService(PowerManager.class); // Hook up power state listener mContext.registerReceiver( Loading Loading @@ -349,6 +355,44 @@ public class SoundTriggerService extends SystemService { } } class MyAppOpsListener implements AppOpsManager.OnOpChangedListener { private final Identity mOriginatorIdentity; private final Consumer<Boolean> mOnOpModeChanged; MyAppOpsListener(Identity originatorIdentity, Consumer<Boolean> onOpModeChanged) { mOriginatorIdentity = Objects.requireNonNull(originatorIdentity); mOnOpModeChanged = Objects.requireNonNull(onOpModeChanged); // Validate package name try { int uid = mPackageManager.getPackageUid(mOriginatorIdentity.packageName, PackageManager.PackageInfoFlags.of(0)); if (uid != mOriginatorIdentity.uid) { throw new SecurityException("Package name: " + mOriginatorIdentity.packageName + "with uid: " + uid + "attempted to spoof as: " + mOriginatorIdentity.uid); } } catch (PackageManager.NameNotFoundException e) { throw new SecurityException("Package name not found: " + mOriginatorIdentity.packageName); } } @Override public void onOpChanged(String op, String packageName) { if (!Objects.equals(op, AppOpsManager.OPSTR_RECORD_AUDIO)) { return; } final int mode = mAppOpsManager.checkOpNoThrow( AppOpsManager.OPSTR_RECORD_AUDIO, mOriginatorIdentity.uid, mOriginatorIdentity.packageName); mOnOpModeChanged.accept(mode == AppOpsManager.MODE_ALLOWED); } void forceOpChangeRefresh() { onOpChanged(AppOpsManager.OPSTR_RECORD_AUDIO, mOriginatorIdentity.packageName); } } class SoundTriggerServiceStub extends ISoundTriggerService.Stub { @Override public ISoundTriggerSession attachAsOriginator(@NonNull Identity originatorIdentity, Loading Loading @@ -463,6 +507,7 @@ public class SoundTriggerService extends SystemService { private final Object mCallbacksLock = new Object(); private final TreeMap<UUID, IRecognitionStatusCallback> mCallbacks = new TreeMap<>(); private final EventLogger mEventLogger; private final MyAppOpsListener mAppOpsListener; SoundTriggerSessionStub(@NonNull IBinder client, SoundTriggerHelper soundTriggerHelper, EventLogger eventLogger) { Loading @@ -479,6 +524,12 @@ public class SoundTriggerService extends SystemService { } mListener = (SoundTriggerDeviceState state) -> mSoundTriggerHelper.onDeviceStateChanged(state); mAppOpsListener = new MyAppOpsListener(mOriginatorIdentity, mSoundTriggerHelper::onAppOpStateChanged); mAppOpsListener.forceOpChangeRefresh(); mAppOpsManager.startWatchingMode(AppOpsManager.OPSTR_RECORD_AUDIO, mOriginatorIdentity.packageName, AppOpsManager.WATCH_FOREGROUND_CHANGES, mAppOpsListener); mDeviceStateHandler.registerListener(mListener); } Loading Loading @@ -930,6 +981,9 @@ public class SoundTriggerService extends SystemService { } private void detach() { if (mAppOpsListener != null) { mAppOpsManager.stopWatchingMode(mAppOpsListener); } mDeviceStateHandler.unregisterListener(mListener); mSoundTriggerHelper.detach(); detachSessionLogger(mEventLogger); Loading @@ -945,9 +999,8 @@ public class SoundTriggerService extends SystemService { } private void enforceDetectionPermissions(ComponentName detectionService) { PackageManager packageManager = mContext.getPackageManager(); String packageName = detectionService.getPackageName(); if (packageManager.checkPermission( if (mPackageManager.checkPermission( Manifest.permission.CAPTURE_AUDIO_HOTWORD, packageName) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException(detectionService.getPackageName() + " does not have" Loading Loading @@ -1635,6 +1688,7 @@ public class SoundTriggerService extends SystemService { private final EventLogger mEventLogger; private final Identity mOriginatorIdentity; private final @NonNull DeviceStateListener mListener; private final MyAppOpsListener mAppOpsListener; private final SparseArray<UUID> mModelUuid = new SparseArray<>(1); Loading @@ -1655,6 +1709,12 @@ public class SoundTriggerService extends SystemService { } mListener = (SoundTriggerDeviceState state) -> mSoundTriggerHelper.onDeviceStateChanged(state); mAppOpsListener = new MyAppOpsListener(mOriginatorIdentity, mSoundTriggerHelper::onAppOpStateChanged); mAppOpsListener.forceOpChangeRefresh(); mAppOpsManager.startWatchingMode(AppOpsManager.OPSTR_RECORD_AUDIO, mOriginatorIdentity.packageName, AppOpsManager.WATCH_FOREGROUND_CHANGES, mAppOpsListener); mDeviceStateHandler.registerListener(mListener); } Loading Loading @@ -1722,6 +1782,9 @@ public class SoundTriggerService extends SystemService { } private void detachInternal() { if (mAppOpsListener != null) { mAppOpsManager.stopWatchingMode(mAppOpsListener); } mEventLogger.enqueue(new SessionEvent(Type.DETACH, null)); detachSessionLogger(mEventLogger); mDeviceStateHandler.unregisterListener(mListener); Loading