Loading services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java +3 −3 Original line number Diff line number Diff line Loading @@ -184,7 +184,7 @@ abstract class DetectorSession { private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool(); // TODO: This may need to be a Handler(looper) final ScheduledExecutorService mScheduledExecutorService; private final AppOpsManager mAppOpsManager; final AppOpsManager mAppOpsManager; final HotwordAudioStreamCopier mHotwordAudioStreamCopier; final AtomicBoolean mUpdateStateAfterStartFinished = new AtomicBoolean(false); final IHotwordRecognitionStatusCallback mCallback; Loading @@ -201,7 +201,7 @@ abstract class DetectorSession { /** Identity used for attributing app ops when delivering data to the Interactor. */ @Nullable private final Identity mVoiceInteractorIdentity; final Identity mVoiceInteractorIdentity; @GuardedBy("mLock") ParcelFileDescriptor mCurrentAudioSink; @GuardedBy("mLock") Loading Loading @@ -926,7 +926,7 @@ abstract class DetectorSession { * @param permission The identifier of the permission we want to check. * @param reason The reason why we're requesting the permission, for auditing purposes. */ private static void enforcePermissionForDataDelivery(@NonNull Context context, protected static void enforcePermissionForDataDelivery(@NonNull Context context, @NonNull Identity identity, @NonNull String permission, @NonNull String reason) { final int status = PermissionUtil.checkPermissionForDataDelivery(context, identity, permission, reason); Loading services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java +85 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,10 @@ package com.android.server.voiceinteraction; import static android.Manifest.permission.CAMERA; import static android.Manifest.permission.RECORD_AUDIO; import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.service.voice.VisualQueryDetectionServiceFailure.ERROR_CODE_ILLEGAL_ATTENTION_STATE; import static android.service.voice.VisualQueryDetectionServiceFailure.ERROR_CODE_ILLEGAL_STREAMING_STATE; Loading @@ -24,6 +28,7 @@ import android.annotation.Nullable; import android.content.Context; import android.media.AudioFormat; import android.media.permission.Identity; import android.os.Binder; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; Loading Loading @@ -58,6 +63,14 @@ import java.util.concurrent.ScheduledExecutorService; final class VisualQueryDetectorSession extends DetectorSession { private static final String TAG = "VisualQueryDetectorSession"; private static final String VISUAL_QUERY_DETECTION_AUDIO_OP_MESSAGE = "Providing query detection result from VisualQueryDetectionService to " + "VoiceInteractionService"; private static final String VISUAL_QUERY_DETECTION_CAMERA_OP_MESSAGE = "Providing query detection result from VisualQueryDetectionService to " + "VoiceInteractionService"; private IVisualQueryDetectionAttentionListener mAttentionListener; private boolean mEgressingData; private boolean mQueryStreaming; Loading Loading @@ -172,6 +185,22 @@ final class VisualQueryDetectorSession extends DetectorSession { "Cannot stream queries without attention signals.")); return; } try { enforcePermissionsForVisualQueryDelivery(RECORD_AUDIO, OP_RECORD_AUDIO, VISUAL_QUERY_DETECTION_AUDIO_OP_MESSAGE); } catch (SecurityException e) { Slog.w(TAG, "Ignoring #onQueryDetected due to a SecurityException", e); try { callback.onVisualQueryDetectionServiceFailure( new VisualQueryDetectionServiceFailure( ERROR_CODE_ILLEGAL_STREAMING_STATE, "Cannot stream queries without audio permission.")); } catch (RemoteException e1) { notifyOnDetectorRemoteException(); throw e1; } return; } mQueryStreaming = true; callback.onQueryDetected(partialQuery); Slog.i(TAG, "Egressed from visual query detection process."); Loading Loading @@ -202,6 +231,48 @@ final class VisualQueryDetectorSession extends DetectorSession { + "enabling the setting.")); return; } // Show camera icon if visual only accessibility data egresses if (partialResult.getAccessibilityDetectionData() != null) { try { enforcePermissionsForVisualQueryDelivery(CAMERA, OP_CAMERA, VISUAL_QUERY_DETECTION_CAMERA_OP_MESSAGE); } catch (SecurityException e) { Slog.w(TAG, "Ignoring #onQueryDetected due to a SecurityException", e); try { callback.onVisualQueryDetectionServiceFailure( new VisualQueryDetectionServiceFailure( ERROR_CODE_ILLEGAL_STREAMING_STATE, "Cannot stream visual only accessibility data " + "without camera permission.")); } catch (RemoteException e1) { notifyOnDetectorRemoteException(); throw e1; } return; } } // Show microphone icon if text query egresses if (!partialResult.getPartialQuery().isEmpty()) { try { enforcePermissionsForVisualQueryDelivery(RECORD_AUDIO, OP_RECORD_AUDIO, VISUAL_QUERY_DETECTION_AUDIO_OP_MESSAGE); } catch (SecurityException e) { Slog.w(TAG, "Ignoring #onQueryDetected due to a SecurityException", e); try { callback.onVisualQueryDetectionServiceFailure( new VisualQueryDetectionServiceFailure( ERROR_CODE_ILLEGAL_STREAMING_STATE, "Cannot stream queries without audio permission.")); } catch (RemoteException e1) { notifyOnDetectorRemoteException(); throw e1; } return; } } mQueryStreaming = true; callback.onResultDetected(partialResult); Slog.i(TAG, "Egressed from visual query detection process."); Loading Loading @@ -280,6 +351,20 @@ final class VisualQueryDetectorSession extends DetectorSession { mEnableAccessibilityDataEgress = enable; } void enforcePermissionsForVisualQueryDelivery(String permission, int op, String msg) { Binder.withCleanCallingIdentity(() -> { synchronized (mLock) { enforcePermissionForDataDelivery(mContext, mVoiceInteractorIdentity, permission, msg); mAppOpsManager.noteOpNoThrow( op, mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName, mVoiceInteractorIdentity.attributionTag, msg); } }); } @SuppressWarnings("GuardedBy") public void dumpLocked(String prefix, PrintWriter pw) { super.dumpLocked(prefix, pw); Loading Loading
services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java +3 −3 Original line number Diff line number Diff line Loading @@ -184,7 +184,7 @@ abstract class DetectorSession { private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool(); // TODO: This may need to be a Handler(looper) final ScheduledExecutorService mScheduledExecutorService; private final AppOpsManager mAppOpsManager; final AppOpsManager mAppOpsManager; final HotwordAudioStreamCopier mHotwordAudioStreamCopier; final AtomicBoolean mUpdateStateAfterStartFinished = new AtomicBoolean(false); final IHotwordRecognitionStatusCallback mCallback; Loading @@ -201,7 +201,7 @@ abstract class DetectorSession { /** Identity used for attributing app ops when delivering data to the Interactor. */ @Nullable private final Identity mVoiceInteractorIdentity; final Identity mVoiceInteractorIdentity; @GuardedBy("mLock") ParcelFileDescriptor mCurrentAudioSink; @GuardedBy("mLock") Loading Loading @@ -926,7 +926,7 @@ abstract class DetectorSession { * @param permission The identifier of the permission we want to check. * @param reason The reason why we're requesting the permission, for auditing purposes. */ private static void enforcePermissionForDataDelivery(@NonNull Context context, protected static void enforcePermissionForDataDelivery(@NonNull Context context, @NonNull Identity identity, @NonNull String permission, @NonNull String reason) { final int status = PermissionUtil.checkPermissionForDataDelivery(context, identity, permission, reason); Loading
services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java +85 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,10 @@ package com.android.server.voiceinteraction; import static android.Manifest.permission.CAMERA; import static android.Manifest.permission.RECORD_AUDIO; import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.service.voice.VisualQueryDetectionServiceFailure.ERROR_CODE_ILLEGAL_ATTENTION_STATE; import static android.service.voice.VisualQueryDetectionServiceFailure.ERROR_CODE_ILLEGAL_STREAMING_STATE; Loading @@ -24,6 +28,7 @@ import android.annotation.Nullable; import android.content.Context; import android.media.AudioFormat; import android.media.permission.Identity; import android.os.Binder; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; Loading Loading @@ -58,6 +63,14 @@ import java.util.concurrent.ScheduledExecutorService; final class VisualQueryDetectorSession extends DetectorSession { private static final String TAG = "VisualQueryDetectorSession"; private static final String VISUAL_QUERY_DETECTION_AUDIO_OP_MESSAGE = "Providing query detection result from VisualQueryDetectionService to " + "VoiceInteractionService"; private static final String VISUAL_QUERY_DETECTION_CAMERA_OP_MESSAGE = "Providing query detection result from VisualQueryDetectionService to " + "VoiceInteractionService"; private IVisualQueryDetectionAttentionListener mAttentionListener; private boolean mEgressingData; private boolean mQueryStreaming; Loading Loading @@ -172,6 +185,22 @@ final class VisualQueryDetectorSession extends DetectorSession { "Cannot stream queries without attention signals.")); return; } try { enforcePermissionsForVisualQueryDelivery(RECORD_AUDIO, OP_RECORD_AUDIO, VISUAL_QUERY_DETECTION_AUDIO_OP_MESSAGE); } catch (SecurityException e) { Slog.w(TAG, "Ignoring #onQueryDetected due to a SecurityException", e); try { callback.onVisualQueryDetectionServiceFailure( new VisualQueryDetectionServiceFailure( ERROR_CODE_ILLEGAL_STREAMING_STATE, "Cannot stream queries without audio permission.")); } catch (RemoteException e1) { notifyOnDetectorRemoteException(); throw e1; } return; } mQueryStreaming = true; callback.onQueryDetected(partialQuery); Slog.i(TAG, "Egressed from visual query detection process."); Loading Loading @@ -202,6 +231,48 @@ final class VisualQueryDetectorSession extends DetectorSession { + "enabling the setting.")); return; } // Show camera icon if visual only accessibility data egresses if (partialResult.getAccessibilityDetectionData() != null) { try { enforcePermissionsForVisualQueryDelivery(CAMERA, OP_CAMERA, VISUAL_QUERY_DETECTION_CAMERA_OP_MESSAGE); } catch (SecurityException e) { Slog.w(TAG, "Ignoring #onQueryDetected due to a SecurityException", e); try { callback.onVisualQueryDetectionServiceFailure( new VisualQueryDetectionServiceFailure( ERROR_CODE_ILLEGAL_STREAMING_STATE, "Cannot stream visual only accessibility data " + "without camera permission.")); } catch (RemoteException e1) { notifyOnDetectorRemoteException(); throw e1; } return; } } // Show microphone icon if text query egresses if (!partialResult.getPartialQuery().isEmpty()) { try { enforcePermissionsForVisualQueryDelivery(RECORD_AUDIO, OP_RECORD_AUDIO, VISUAL_QUERY_DETECTION_AUDIO_OP_MESSAGE); } catch (SecurityException e) { Slog.w(TAG, "Ignoring #onQueryDetected due to a SecurityException", e); try { callback.onVisualQueryDetectionServiceFailure( new VisualQueryDetectionServiceFailure( ERROR_CODE_ILLEGAL_STREAMING_STATE, "Cannot stream queries without audio permission.")); } catch (RemoteException e1) { notifyOnDetectorRemoteException(); throw e1; } return; } } mQueryStreaming = true; callback.onResultDetected(partialResult); Slog.i(TAG, "Egressed from visual query detection process."); Loading Loading @@ -280,6 +351,20 @@ final class VisualQueryDetectorSession extends DetectorSession { mEnableAccessibilityDataEgress = enable; } void enforcePermissionsForVisualQueryDelivery(String permission, int op, String msg) { Binder.withCleanCallingIdentity(() -> { synchronized (mLock) { enforcePermissionForDataDelivery(mContext, mVoiceInteractorIdentity, permission, msg); mAppOpsManager.noteOpNoThrow( op, mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName, mVoiceInteractorIdentity.attributionTag, msg); } }); } @SuppressWarnings("GuardedBy") public void dumpLocked(String prefix, PrintWriter pw) { super.dumpLocked(prefix, pw); Loading