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

Commit 243637f9 authored by Charles Chen's avatar Charles Chen Committed by Android (Google) Code Review
Browse files

Merge "[API] Introduce API for accessibility data stream" into main

parents bf9418b0 307d533c
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -13453,6 +13453,7 @@ package android.service.voice {
  @FlaggedApi("android.service.voice.flags.allow_complex_results_egress_from_vqds") public final class VisualQueryDetectedResult implements android.os.Parcelable {
    method public int describeContents();
    method @Nullable public byte[] getAccessibilityDetectionData();
    method public static int getMaxSpeakerId();
    method @NonNull public String getPartialQuery();
    method public int getSpeakerId();
@@ -13463,6 +13464,7 @@ package android.service.voice {
  public static final class VisualQueryDetectedResult.Builder {
    ctor public VisualQueryDetectedResult.Builder();
    method @NonNull public android.service.voice.VisualQueryDetectedResult build();
    method @NonNull public android.service.voice.VisualQueryDetectedResult.Builder setAccessibilityDetectionData(@NonNull byte...);
    method @NonNull public android.service.voice.VisualQueryDetectedResult.Builder setPartialQuery(@NonNull String);
    method @NonNull public android.service.voice.VisualQueryDetectedResult.Builder setSpeakerId(int);
  }
@@ -13500,7 +13502,10 @@ package android.service.voice {
  }
  public class VisualQueryDetector {
    method @FlaggedApi("android.service.voice.flags.allow_complex_results_egress_from_vqds") public void clearAccessibilityDetectionEnabledListener();
    method public void destroy();
    method @FlaggedApi("android.service.voice.flags.allow_complex_results_egress_from_vqds") public boolean isAccessibilityDetectionEnabled();
    method @FlaggedApi("android.service.voice.flags.allow_complex_results_egress_from_vqds") public void setAccessibilityDetectionEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
    method @RequiresPermission(allOf={android.Manifest.permission.CAMERA, android.Manifest.permission.RECORD_AUDIO}) public boolean startRecognition();
    method @RequiresPermission(allOf={android.Manifest.permission.CAMERA, android.Manifest.permission.RECORD_AUDIO}) public boolean stopRecognition();
    method public void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory);
+64 −9
Original line number Diff line number Diff line
@@ -68,6 +68,22 @@ public final class VisualQueryDetectedResult implements Parcelable {
        return 15;
    }

    /**
     * Detected signal representing the arbitrary data that will make accessibility detections work.
     *
     * This field should only be set if the device setting for allowing accessibility data is on
     * based on the result of {@link VisualQueryDetector#isAccessibilityDetectionEnabled()}. If the
     * enable bit return by the method is {@code false}, it would suggest a failure to egress the
     * {@link VisualQueryDetectedResult} object with this field set. The system server will prevent
     * egress and invoke
     * {@link VisualQueryDetector.Callback#onFailure(VisualQueryDetectionServiceFailure)}.
     */
    @Nullable
    private final byte[] mAccessibilityDetectionData;
    private static byte[] defaultAccessibilityDetectionData() {
        return null;
    }

    private void onConstructed() {
        Preconditions.checkArgumentInRange(mSpeakerId, 0, getMaxSpeakerId(), "speakerId");
    }
@@ -78,7 +94,10 @@ public final class VisualQueryDetectedResult implements Parcelable {
     * @hide
     */
    public Builder buildUpon() {
        return new Builder().setPartialQuery(mPartialQuery).setSpeakerId(mSpeakerId);
        return new Builder()
                .setPartialQuery(mPartialQuery)
                .setSpeakerId(mSpeakerId)
                .setAccessibilityDetectionData(mAccessibilityDetectionData);
    }


@@ -98,11 +117,13 @@ public final class VisualQueryDetectedResult implements Parcelable {
    @DataClass.Generated.Member
    /* package-private */ VisualQueryDetectedResult(
            @NonNull String partialQuery,
            int speakerId) {
            int speakerId,
            @Nullable byte[] accessibilityDetectionData) {
        this.mPartialQuery = partialQuery;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mPartialQuery);
        this.mSpeakerId = speakerId;
        this.mAccessibilityDetectionData = accessibilityDetectionData;

        onConstructed();
    }
@@ -125,6 +146,16 @@ public final class VisualQueryDetectedResult implements Parcelable {
        return mSpeakerId;
    }

    /**
     * Detected signal representing the data for allowing accessibility feature. This field can
     * only be set when the secure device settings is set to true by either settings page UI or
     * {@link VisualQueryDetector@setAccessibilityDetectionEnabled(boolean)}
     */
    @DataClass.Generated.Member
    public @Nullable byte[] getAccessibilityDetectionData() {
        return mAccessibilityDetectionData;
    }

    @Override
    @DataClass.Generated.Member
    public String toString() {
@@ -133,7 +164,8 @@ public final class VisualQueryDetectedResult implements Parcelable {

        return "VisualQueryDetectedResult { " +
                "partialQuery = " + mPartialQuery + ", " +
                "speakerId = " + mSpeakerId +
                "speakerId = " + mSpeakerId + ", " +
                "accessibilityDetectionData = " + java.util.Arrays.toString(mAccessibilityDetectionData) +
        " }";
    }

@@ -151,7 +183,8 @@ public final class VisualQueryDetectedResult implements Parcelable {
        //noinspection PointlessBooleanExpression
        return true
                && Objects.equals(mPartialQuery, that.mPartialQuery)
                && mSpeakerId == that.mSpeakerId;
                && mSpeakerId == that.mSpeakerId
                && java.util.Arrays.equals(mAccessibilityDetectionData, that.mAccessibilityDetectionData);
    }

    @Override
@@ -163,6 +196,7 @@ public final class VisualQueryDetectedResult implements Parcelable {
        int _hash = 1;
        _hash = 31 * _hash + Objects.hashCode(mPartialQuery);
        _hash = 31 * _hash + mSpeakerId;
        _hash = 31 * _hash + java.util.Arrays.hashCode(mAccessibilityDetectionData);
        return _hash;
    }

@@ -174,6 +208,7 @@ public final class VisualQueryDetectedResult implements Parcelable {

        dest.writeString(mPartialQuery);
        dest.writeInt(mSpeakerId);
        dest.writeByteArray(mAccessibilityDetectionData);
    }

    @Override
@@ -189,11 +224,13 @@ public final class VisualQueryDetectedResult implements Parcelable {

        String partialQuery = in.readString();
        int speakerId = in.readInt();
        byte[] accessibilityDetectionData = in.createByteArray();

        this.mPartialQuery = partialQuery;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mPartialQuery);
        this.mSpeakerId = speakerId;
        this.mAccessibilityDetectionData = accessibilityDetectionData;

        onConstructed();
    }
@@ -221,6 +258,7 @@ public final class VisualQueryDetectedResult implements Parcelable {

        private @NonNull String mPartialQuery;
        private int mSpeakerId;
        private @Nullable byte[] mAccessibilityDetectionData;

        private long mBuilderFieldsSet = 0L;

@@ -251,10 +289,23 @@ public final class VisualQueryDetectedResult implements Parcelable {
            return this;
        }

        /**
         * Detected signal representing the data for allowing accessibility feature. This field can
         * only be set when the secure device settings is set to true by either settings page UI or
         * {@link VisualQueryDetector@setAccessibilityDetectionEnabled(boolean)}
         */
        @DataClass.Generated.Member
        public @NonNull Builder setAccessibilityDetectionData(@NonNull byte... value) {
            checkNotUsed();
            mBuilderFieldsSet |= 0x4;
            mAccessibilityDetectionData = value;
            return this;
        }

        /** Builds the instance. This builder should not be touched after calling this! */
        public @NonNull VisualQueryDetectedResult build() {
            checkNotUsed();
            mBuilderFieldsSet |= 0x4; // Mark builder used
            mBuilderFieldsSet |= 0x8; // Mark builder used

            if ((mBuilderFieldsSet & 0x1) == 0) {
                mPartialQuery = defaultPartialQuery();
@@ -262,14 +313,18 @@ public final class VisualQueryDetectedResult implements Parcelable {
            if ((mBuilderFieldsSet & 0x2) == 0) {
                mSpeakerId = defaultSpeakerId();
            }
            if ((mBuilderFieldsSet & 0x4) == 0) {
                mAccessibilityDetectionData = defaultAccessibilityDetectionData();
            }
            VisualQueryDetectedResult o = new VisualQueryDetectedResult(
                    mPartialQuery,
                    mSpeakerId);
                    mSpeakerId,
                    mAccessibilityDetectionData);
            return o;
        }

        private void checkNotUsed() {
            if ((mBuilderFieldsSet & 0x4) != 0) {
            if ((mBuilderFieldsSet & 0x8) != 0) {
                throw new IllegalStateException(
                        "This Builder should not be reused. Use a new Builder instance instead");
            }
@@ -277,10 +332,10 @@ public final class VisualQueryDetectedResult implements Parcelable {
    }

    @DataClass.Generated(
            time = 1704949386772L,
            time = 1707429290528L,
            codegenVersion = "1.0.23",
            sourceFile = "frameworks/base/core/java/android/service/voice/VisualQueryDetectedResult.java",
            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPartialQuery\nprivate final  int mSpeakerId\nprivate static  java.lang.String defaultPartialQuery()\nprivate static  int defaultSpeakerId()\npublic static  int getMaxSpeakerId()\nprivate  void onConstructed()\npublic  android.service.voice.VisualQueryDetectedResult.Builder buildUpon()\nclass VisualQueryDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPartialQuery\nprivate final  int mSpeakerId\nprivate final @android.annotation.Nullable byte[] mAccessibilityDetectionData\nprivate static  java.lang.String defaultPartialQuery()\nprivate static  int defaultSpeakerId()\npublic static  int getMaxSpeakerId()\nprivate static  byte[] defaultAccessibilityDetectionData()\nprivate  void onConstructed()\npublic  android.service.voice.VisualQueryDetectedResult.Builder buildUpon()\nclass VisualQueryDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
    @Deprecated
    private void __metadata() {}

+107 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.text.TextUtils;
import android.util.Slog;

import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVoiceInteractionAccessibilitySettingsListener;
import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.infra.AndroidFuture;

@@ -61,6 +62,8 @@ import java.util.function.Consumer;
public class VisualQueryDetector {
    private static final String TAG = VisualQueryDetector.class.getSimpleName();
    private static final boolean DEBUG = false;
    private static final int SETTINGS_DISABLE_BIT = 0;
    private static final int SETTINGS_ENABLE_BIT = 1;

    private final Callback mCallback;
    private final Executor mExecutor;
@@ -68,6 +71,8 @@ public class VisualQueryDetector {
    private final IVoiceInteractionManagerService mManagerService;
    private final VisualQueryDetectorInitializationDelegate mInitializationDelegate;
    private final String mAttributionTag;
    // Used to manage the internal mapping of exposed listener API and internal aidl impl
    private AccessibilityDetectionEnabledListenerWrapper mActiveAccessibilityListenerWrapper = null;

    VisualQueryDetector(
            IVoiceInteractionManagerService managerService,
@@ -174,6 +179,108 @@ public class VisualQueryDetector {
        }
    }

    /**
     * Gets the binary value that controls the egress of accessibility data from
     * {@link VisualQueryDetectedResult#setAccessibilityDetectionData(byte[])} is enabled.
     *
     * @return boolean value denoting if the setting is on. Default is {@code false}.
     * @hide
     */
    @SystemApi
    @FlaggedApi(Flags.FLAG_ALLOW_COMPLEX_RESULTS_EGRESS_FROM_VQDS)
    public boolean isAccessibilityDetectionEnabled() {
        Slog.d(TAG, "Fetching accessibility setting");
        synchronized (mInitializationDelegate.getLock()) {
            try {
                return mManagerService.getAccessibilityDetectionEnabled();
            } catch (RemoteException e) {
                e.rethrowFromSystemServer();
            }
            return false;
        }
    }

    /**
     * Sets a listener subscribing to the value of the system setting that controls the egress of
     * accessibility data from
     * {@link VisualQueryDetectedResult#setAccessibilityDetectionData(byte[])} is enabled.
     *
     * Only one listener can be set at a time. The listener set must be unset with
     * {@link clearAccessibilityDetectionEnabledListener(Consumer<Boolean>)}
     * in order to set a new listener. Otherwise, this method will throw a
     * {@link IllegalStateException}.
     *
     * @param listener Listener of type {@code Consumer<Boolean>} to subscribe to the value update.
     * @hide
     */
    @SystemApi
    @FlaggedApi(Flags.FLAG_ALLOW_COMPLEX_RESULTS_EGRESS_FROM_VQDS)
    public void setAccessibilityDetectionEnabledListener(@NonNull Consumer<Boolean> listener) {
        Slog.d(TAG, "Registering Accessibility settings listener.");
        synchronized (mInitializationDelegate.getLock()) {
            try {
                if (mActiveAccessibilityListenerWrapper != null) {
                    Slog.e(TAG, "Fail to register accessibility setting listener: "
                            + "already registered and not unregistered.");
                    throw new IllegalStateException(
                            "Cannot register listener with listeners already set.");
                }
                mActiveAccessibilityListenerWrapper =
                        new AccessibilityDetectionEnabledListenerWrapper(listener);
                mManagerService.registerAccessibilityDetectionSettingsListener(
                        mActiveAccessibilityListenerWrapper);
            } catch (RemoteException e) {
                e.rethrowFromSystemServer();
            }
        }
    }

    /**
     * Clear the listener that has been set with
     * {@link setAccessibilityDetectionEnabledListener(Consumer<Boolean>)} such that when the value
     * of the setting that controls the egress of accessibility data is changed the listener gets
     * notified.
     *
     * If there is not listener that has been registered, the call to this method will lead to a
     * {@link IllegalStateException}.
     *
     * @hide
     */
    @SystemApi
    @FlaggedApi(Flags.FLAG_ALLOW_COMPLEX_RESULTS_EGRESS_FROM_VQDS)
    public void clearAccessibilityDetectionEnabledListener() {
        Slog.d(TAG, "Unregistering Accessibility settings listener.");
        synchronized (mInitializationDelegate.getLock()) {
            try {
                if (mActiveAccessibilityListenerWrapper == null) {
                    Slog.e(TAG, "Not able to remove the listener: listener does not exist.");
                    throw new IllegalStateException("Cannot clear listener since it is not set.");
                }
                mManagerService.unregisterAccessibilityDetectionSettingsListener(
                        mActiveAccessibilityListenerWrapper);
                mActiveAccessibilityListenerWrapper = null;
            } catch (RemoteException e) {
                e.rethrowFromSystemServer();
            }
        }
    }


    private final class AccessibilityDetectionEnabledListenerWrapper
            extends IVoiceInteractionAccessibilitySettingsListener.Stub {

        private Consumer<Boolean> mListener;

        AccessibilityDetectionEnabledListenerWrapper(Consumer<Boolean> listener) {
            mListener = listener;
        }

        @Override
        public void onAccessibilityDetectionChanged(boolean enabled) {
            mListener.accept(enabled);
        }
    }

    /** @hide */
    public void dump(String prefix, PrintWriter pw) {
        synchronized (mInitializationDelegate.getLock()) {
+24 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.app;

oneway interface IVoiceInteractionAccessibilitySettingsListener {
   /**
    * Called when the value of secure setting has changed.
    */
   void onAccessibilityDetectionChanged(boolean enable);
}
+18 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.service.voice.VisibleActivityInfo;

import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.internal.app.IVoiceInteractionAccessibilitySettingsListener;
import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractionSoundTriggerSession;
@@ -382,4 +383,21 @@ interface IVoiceInteractionManagerService {
    oneway void notifyActivityEventChanged(
            in IBinder activityToken,
            int type);

    /**
     * rely on the system server to get the secure settings
     */
    boolean getAccessibilityDetectionEnabled();

    /**
     * register the listener
     */
    oneway void registerAccessibilityDetectionSettingsListener(
            in IVoiceInteractionAccessibilitySettingsListener listener);

    /**
     * unregister the listener
     */
     oneway void unregisterAccessibilityDetectionSettingsListener(
            in IVoiceInteractionAccessibilitySettingsListener listener);
}
Loading