Loading services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java +5 −13 Original line number Diff line number Diff line Loading @@ -133,13 +133,8 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware * Throws a {@link SecurityException} iff the originator has permission to receive data. */ void enforcePermissionsForDataDelivery(@NonNull Identity identity, @NonNull String reason) { // TODO(b/186164881): remove // START TEMP HACK enforcePermissionForPreflight(mContext, identity, RECORD_AUDIO); int hotwordOp = AppOpsManager.strOpToOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD); mContext.getSystemService(AppOpsManager.class).noteOpNoThrow(hotwordOp, identity.uid, identity.packageName, identity.attributionTag, reason); // END TEMP HACK enforcePermissionForDataDelivery(mContext, identity, RECORD_AUDIO, reason); enforcePermissionForDataDelivery(mContext, identity, CAPTURE_AUDIO_HOTWORD, reason); } Loading Loading @@ -167,8 +162,8 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware /** * Throws a {@link SecurityException} if originator permanently doesn't have the given * permission, or a {@link ServiceSpecificException} with a {@link * Status#TEMPORARY_PERMISSION_DENIED} if caller originator doesn't have the given permission. * permission. * Soft (temporary) denials are considered OK for preflight purposes. * * @param context A {@link Context}, used for permission checks. * @param identity The identity to check. Loading @@ -180,15 +175,12 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware permission); switch (status) { case PermissionChecker.PERMISSION_GRANTED: case PermissionChecker.PERMISSION_SOFT_DENIED: return; case PermissionChecker.PERMISSION_HARD_DENIED: throw new SecurityException( String.format("Failed to obtain permission %s for identity %s", permission, ObjectPrinter.print(identity, true, 16))); case PermissionChecker.PERMISSION_SOFT_DENIED: throw new ServiceSpecificException(Status.TEMPORARY_PERMISSION_DENIED, String.format("Failed to obtain permission %s for identity %s", permission, ObjectPrinter.print(identity, true, 16))); default: throw new RuntimeException("Unexpected perimission check result."); } Loading services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java +40 −0 Original line number Diff line number Diff line Loading @@ -318,6 +318,8 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware */ private Map<Integer, ModelParameterRange> parameterSupport = new HashMap<>(); private RecognitionConfig mConfig; /** * Check that the given parameter is known to be supported for this model. * Loading Loading @@ -369,6 +371,14 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware void setActivityState(Activity activity) { mActivityState.set(activity.ordinal()); } void setRecognitionConfig(@NonNull RecognitionConfig config) { mConfig = config; } RecognitionConfig getRecognitionConfig() { return mConfig; } } /** Loading Loading @@ -502,6 +512,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware // Normally, we would set the state after the operation succeeds. However, since // the activity state may be reset outside of the lock, we set it here first, // and reset it in case of exception. modelState.setRecognitionConfig(config); modelState.setActivityState(ModelState.Activity.ACTIVE); mDelegate.startRecognition(modelHandle, config); } catch (Exception e) { Loading Loading @@ -542,6 +553,27 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware } } private void restartIfIntercepted(int modelHandle) { synchronized (SoundTriggerMiddlewareValidation.this) { // State validation. if (mState == ModuleStatus.DETACHED) { return; } ModelState modelState = mLoadedModels.get(modelHandle); if (modelState == null || modelState.getActivityState() != ModelState.Activity.INTERCEPTED) { return; } try { mDelegate.startRecognition(modelHandle, modelState.getRecognitionConfig()); modelState.setActivityState(ModelState.Activity.ACTIVE); Log.i(TAG, "Restarted intercepted model " + modelHandle); } catch (Exception e) { Log.i(TAG, "Failed to restart intercepted model " + modelHandle, e); } } } @Override public void forceRecognitionEvent(int modelHandle) { // Input validation (always valid). Loading Loading @@ -753,6 +785,10 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware Log.e(TAG, "Client callback exception.", e); if (event.status != RecognitionStatus.FORCED) { modelState.setActivityState(ModelState.Activity.INTERCEPTED); // If we failed to deliver an actual event to the client, they would never // know to restart it whenever circumstances change. Thus, we restart it // here. We do this from a separate thread to avoid any race conditions. new Thread(() -> restartIfIntercepted(modelHandle)).start(); } } } Loading Loading @@ -780,6 +816,10 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware Log.e(TAG, "Client callback exception.", e); if (event.common.status != RecognitionStatus.FORCED) { modelState.setActivityState(ModelState.Activity.INTERCEPTED); // If we failed to deliver an actual event to the client, they would never // know to restart it whenever circumstances change. Thus, we restart it // here. We do this from a separate thread to avoid any race conditions. new Thread(() -> restartIfIntercepted(modelHandle)).start(); } } } Loading Loading
services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java +5 −13 Original line number Diff line number Diff line Loading @@ -133,13 +133,8 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware * Throws a {@link SecurityException} iff the originator has permission to receive data. */ void enforcePermissionsForDataDelivery(@NonNull Identity identity, @NonNull String reason) { // TODO(b/186164881): remove // START TEMP HACK enforcePermissionForPreflight(mContext, identity, RECORD_AUDIO); int hotwordOp = AppOpsManager.strOpToOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD); mContext.getSystemService(AppOpsManager.class).noteOpNoThrow(hotwordOp, identity.uid, identity.packageName, identity.attributionTag, reason); // END TEMP HACK enforcePermissionForDataDelivery(mContext, identity, RECORD_AUDIO, reason); enforcePermissionForDataDelivery(mContext, identity, CAPTURE_AUDIO_HOTWORD, reason); } Loading Loading @@ -167,8 +162,8 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware /** * Throws a {@link SecurityException} if originator permanently doesn't have the given * permission, or a {@link ServiceSpecificException} with a {@link * Status#TEMPORARY_PERMISSION_DENIED} if caller originator doesn't have the given permission. * permission. * Soft (temporary) denials are considered OK for preflight purposes. * * @param context A {@link Context}, used for permission checks. * @param identity The identity to check. Loading @@ -180,15 +175,12 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware permission); switch (status) { case PermissionChecker.PERMISSION_GRANTED: case PermissionChecker.PERMISSION_SOFT_DENIED: return; case PermissionChecker.PERMISSION_HARD_DENIED: throw new SecurityException( String.format("Failed to obtain permission %s for identity %s", permission, ObjectPrinter.print(identity, true, 16))); case PermissionChecker.PERMISSION_SOFT_DENIED: throw new ServiceSpecificException(Status.TEMPORARY_PERMISSION_DENIED, String.format("Failed to obtain permission %s for identity %s", permission, ObjectPrinter.print(identity, true, 16))); default: throw new RuntimeException("Unexpected perimission check result."); } Loading
services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java +40 −0 Original line number Diff line number Diff line Loading @@ -318,6 +318,8 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware */ private Map<Integer, ModelParameterRange> parameterSupport = new HashMap<>(); private RecognitionConfig mConfig; /** * Check that the given parameter is known to be supported for this model. * Loading Loading @@ -369,6 +371,14 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware void setActivityState(Activity activity) { mActivityState.set(activity.ordinal()); } void setRecognitionConfig(@NonNull RecognitionConfig config) { mConfig = config; } RecognitionConfig getRecognitionConfig() { return mConfig; } } /** Loading Loading @@ -502,6 +512,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware // Normally, we would set the state after the operation succeeds. However, since // the activity state may be reset outside of the lock, we set it here first, // and reset it in case of exception. modelState.setRecognitionConfig(config); modelState.setActivityState(ModelState.Activity.ACTIVE); mDelegate.startRecognition(modelHandle, config); } catch (Exception e) { Loading Loading @@ -542,6 +553,27 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware } } private void restartIfIntercepted(int modelHandle) { synchronized (SoundTriggerMiddlewareValidation.this) { // State validation. if (mState == ModuleStatus.DETACHED) { return; } ModelState modelState = mLoadedModels.get(modelHandle); if (modelState == null || modelState.getActivityState() != ModelState.Activity.INTERCEPTED) { return; } try { mDelegate.startRecognition(modelHandle, modelState.getRecognitionConfig()); modelState.setActivityState(ModelState.Activity.ACTIVE); Log.i(TAG, "Restarted intercepted model " + modelHandle); } catch (Exception e) { Log.i(TAG, "Failed to restart intercepted model " + modelHandle, e); } } } @Override public void forceRecognitionEvent(int modelHandle) { // Input validation (always valid). Loading Loading @@ -753,6 +785,10 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware Log.e(TAG, "Client callback exception.", e); if (event.status != RecognitionStatus.FORCED) { modelState.setActivityState(ModelState.Activity.INTERCEPTED); // If we failed to deliver an actual event to the client, they would never // know to restart it whenever circumstances change. Thus, we restart it // here. We do this from a separate thread to avoid any race conditions. new Thread(() -> restartIfIntercepted(modelHandle)).start(); } } } Loading Loading @@ -780,6 +816,10 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware Log.e(TAG, "Client callback exception.", e); if (event.common.status != RecognitionStatus.FORCED) { modelState.setActivityState(ModelState.Activity.INTERCEPTED); // If we failed to deliver an actual event to the client, they would never // know to restart it whenever circumstances change. Thus, we restart it // here. We do this from a separate thread to avoid any race conditions. new Thread(() -> restartIfIntercepted(modelHandle)).start(); } } } Loading