Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 22943b74 authored by Charles Chen's avatar Charles Chen
Browse files

Note record audio and camera OP when vqds egress

Privacy patch - we need to make sure the privacy indicator icons are
shown for queries being egress even if it is in the form of text, or
pure visual signals.

Bug: 319273258
Test: atest CtsVoiceInteractionTestCases & Manual - icon shows
Flag: N/A
Change-Id: I1095978c1fb7e5739de5c191fc0be07614d097ba
parent 1914e734
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -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;
@@ -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")
@@ -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);
+85 −0
Original line number Diff line number Diff line
@@ -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;

@@ -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;
@@ -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;
@@ -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.");
@@ -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.");
@@ -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);