diff options
| author | 2022-10-13 01:12:10 +0000 | |
|---|---|---|
| committer | 2022-11-15 18:27:12 +0000 | |
| commit | a8479861281298c987ce979144ef5a132445b251 (patch) | |
| tree | 2c3b3a69d7f0078e60b0fc9c193fcd837f29ae62 | |
| parent | c9de1e5273d048a5ae6fe5352c0ce7989f22a49d (diff) | |
Update the proximity feature to return proximity levels
The original proximity feature which returned the distance in meters was
determined to be too privacy-invasive. AttentionManagerService continue
to receive proximity in distance from Attention Service, but
HotwordDetectedResult will map it
to different proximity levels, near and far. These proximity levels will be used in HotwordDetectionConnection. Use a RequiredServiceRule to make sure we only run the test if the device has the attention service
Bug: 253326426
Test: atest CtsVoiceInteractionTestCases
Change-Id: I4d55564c39d48dd1869d747beb37f81170c102f6
Merged-In: I4d55564c39d48dd1869d747beb37f81170c102f6
3 files changed, 133 insertions, 38 deletions
diff --git a/core/java/android/service/voice/HotwordDetectedResult.java b/core/java/android/service/voice/HotwordDetectedResult.java index 990e136197d9..c3d58b7b3b23 100644 --- a/core/java/android/service/voice/HotwordDetectedResult.java +++ b/core/java/android/service/voice/HotwordDetectedResult.java @@ -32,6 +32,8 @@ import com.android.internal.R; import com.android.internal.util.DataClass; import com.android.internal.util.Preconditions; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -100,14 +102,42 @@ public final class HotwordDetectedResult implements Parcelable { private static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE = 63; /** - * The bundle key for proximity value + * The bundle key for proximity * * TODO(b/238896013): Move the proximity logic out of bundle to proper API. + */ + private static final String EXTRA_PROXIMITY = + "android.service.voice.extra.PROXIMITY"; + + /** + * Users’ proximity is unknown (proximity sensing was inconclusive and is unsupported). + * + * @hide + */ + public static final int PROXIMITY_UNKNOWN = -1; + + /** + * Proximity value that represents that the object is near. + * + * @hide + */ + public static final int PROXIMITY_NEAR = 1; + + /** + * Proximity value that represents that the object is far. * * @hide */ - public static final String EXTRA_PROXIMITY_METERS = - "android.service.voice.extra.PROXIMITY_METERS"; + public static final int PROXIMITY_FAR = 2; + + /** @hide */ + @IntDef(prefix = {"PROXIMITY"}, value = { + PROXIMITY_UNKNOWN, + PROXIMITY_NEAR, + PROXIMITY_FAR + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ProximityValue {} /** Confidence level in the trigger outcome. */ @HotwordConfidenceLevelValue @@ -223,12 +253,14 @@ public final class HotwordDetectedResult implements Parcelable { * versions of Android. * * <p>After the trigger happens, a special case of proximity-related extra, with the key of - * 'android.service.voice.extra.PROXIMITY_METERS' and the value of distance in meters (double), - * will be stored to enable proximity logic. The proximity meters is provided by the system, - * on devices that support detecting proximity of nearby users, to help disambiguate which - * nearby device should respond. When the proximity is unknown, the proximity value will not - * be stored. This mapping will be excluded from the max bundle size calculation because this - * mapping is included after the result is returned from the hotword detector service. + * 'android.service.voice.extra.PROXIMITY_VALUE' and the value of proximity value (integer) + * will be stored to enable proximity logic. {@link HotwordDetectedResult#PROXIMITY_NEAR} will + * indicate 'NEAR' proximity and {@link HotwordDetectedResult#PROXIMITY_FAR} will indicate 'FAR' + * proximity. The proximity value is provided by the system, on devices that support detecting + * proximity of nearby users, to help disambiguate which nearby device should respond. When the + * proximity is unknown, the proximity value will not be stored. This mapping will be excluded + * from the max bundle size calculation because this mapping is included after the result is + * returned from the hotword detector service. * * <p>This is a PersistableBundle so it doesn't allow any remotable objects or other contents * that can be used to communicate with other processes. @@ -351,16 +383,16 @@ public final class HotwordDetectedResult implements Parcelable { // Remove the proximity key from the bundle before checking the bundle size. The // proximity value is added after the privileged module and can avoid the // maxBundleSize limitation. - if (mExtras.containsKey(EXTRA_PROXIMITY_METERS)) { - double proximityMeters = mExtras.getDouble(EXTRA_PROXIMITY_METERS); - mExtras.remove(EXTRA_PROXIMITY_METERS); + if (mExtras.containsKey(EXTRA_PROXIMITY)) { + int proximityValue = mExtras.getInt(EXTRA_PROXIMITY); + mExtras.remove(EXTRA_PROXIMITY); // Skip checking parcelable size if the new bundle size is 0. Newly empty bundle // has parcelable size of 4, but the default bundle has parcelable size of 0. if (mExtras.size() > 0) { Preconditions.checkArgumentInRange(getParcelableSize(mExtras), 0, getMaxBundleSize(), "extras"); } - mExtras.putDouble(EXTRA_PROXIMITY_METERS, proximityMeters); + mExtras.putInt(EXTRA_PROXIMITY, proximityValue); } else { Preconditions.checkArgumentInRange(getParcelableSize(mExtras), 0, getMaxBundleSize(), "extras"); @@ -416,6 +448,42 @@ public final class HotwordDetectedResult implements Parcelable { .setExtras(mExtras); } + /** + * Adds proximity level, either near or far, that is mapped for the given distance into + * the bundle. The proximity value is provided by the system, on devices that support detecting + * proximity of nearby users, to help disambiguate which nearby device should respond. + * This mapping will be excluded from the max bundle size calculation because this mapping is + * included after the result is returned from the hotword detector service. The value will not + * be included if the proximity was unknown. + * + * @hide + */ + public void setProximity(double distance) { + int proximityLevel = convertToProximityLevel(distance); + if (proximityLevel != PROXIMITY_UNKNOWN) { + mExtras.putInt(EXTRA_PROXIMITY, proximityLevel); + } + } + + /** + * Mapping of the proximity distance (meters) to proximity values, unknown, near, and far. + * Currently, this mapping is handled by HotwordDetectedResult because it handles just + * HotwordDetectionConnection which we know the mapping of. However, the mapping will need to + * move to a more centralized place once there are more clients. + * + * TODO(b/258531144): Move the proximity mapping to a central location + */ + @ProximityValue + private int convertToProximityLevel(double distance) { + if (distance < 0) { + return PROXIMITY_UNKNOWN; + } else if (distance <= 3) { + return PROXIMITY_NEAR; + } else { + return PROXIMITY_FAR; + } + } + // Code below generated by codegen v1.0.23. @@ -441,7 +509,7 @@ public final class HotwordDetectedResult implements Parcelable { CONFIDENCE_LEVEL_HIGH, CONFIDENCE_LEVEL_VERY_HIGH }) - @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) + @Retention(RetentionPolicy.SOURCE) @DataClass.Generated.Member public @interface ConfidenceLevel {} @@ -472,7 +540,7 @@ public final class HotwordDetectedResult implements Parcelable { LIMIT_HOTWORD_OFFSET_MAX_VALUE, LIMIT_AUDIO_CHANNEL_MAX_VALUE }) - @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) + @Retention(RetentionPolicy.SOURCE) @DataClass.Generated.Member /* package-private */ @interface Limit {} @@ -488,6 +556,30 @@ public final class HotwordDetectedResult implements Parcelable { } } + /** @hide */ + @IntDef(prefix = "PROXIMITY_", value = { + PROXIMITY_UNKNOWN, + PROXIMITY_NEAR, + PROXIMITY_FAR + }) + @Retention(RetentionPolicy.SOURCE) + @DataClass.Generated.Member + public @interface Proximity {} + + /** @hide */ + @DataClass.Generated.Member + public static String proximityToString(@Proximity int value) { + switch (value) { + case PROXIMITY_UNKNOWN: + return "PROXIMITY_UNKNOWN"; + case PROXIMITY_NEAR: + return "PROXIMITY_NEAR"; + case PROXIMITY_FAR: + return "PROXIMITY_FAR"; + default: return Integer.toHexString(value); + } + } + @DataClass.Generated.Member /* package-private */ HotwordDetectedResult( @HotwordConfidenceLevelValue int confidenceLevel, @@ -614,12 +706,14 @@ public final class HotwordDetectedResult implements Parcelable { * versions of Android. * * <p>After the trigger happens, a special case of proximity-related extra, with the key of - * 'android.service.voice.extra.PROXIMITY_METERS' and the value of distance in meters (double), - * will be stored to enable proximity logic. The proximity meters is provided by the system, - * on devices that support detecting proximity of nearby users, to help disambiguate which - * nearby device should respond. When the proximity is unknown, the proximity value will not - * be stored. This mapping will be excluded from the max bundle size calculation because this - * mapping is included after the result is returned from the hotword detector service. + * 'android.service.voice.extra.PROXIMITY_VALUE' and the value of proximity value (integer) + * will be stored to enable proximity logic. {@link HotwordDetectedResult#PROXIMITY_NEAR} will + * indicate 'NEAR' proximity and {@link HotwordDetectedResult#PROXIMITY_FAR} will indicate 'FAR' + * proximity. The proximity value is provided by the system, on devices that support detecting + * proximity of nearby users, to help disambiguate which nearby device should respond. When the + * proximity is unknown, the proximity value will not be stored. This mapping will be excluded + * from the max bundle size calculation because this mapping is included after the result is + * returned from the hotword detector service. * * <p>This is a PersistableBundle so it doesn't allow any remotable objects or other contents * that can be used to communicate with other processes. @@ -932,12 +1026,14 @@ public final class HotwordDetectedResult implements Parcelable { * versions of Android. * * <p>After the trigger happens, a special case of proximity-related extra, with the key of - * 'android.service.voice.extra.PROXIMITY_METERS' and the value of distance in meters (double), - * will be stored to enable proximity logic. The proximity meters is provided by the system, - * on devices that support detecting proximity of nearby users, to help disambiguate which - * nearby device should respond. When the proximity is unknown, the proximity value will not - * be stored. This mapping will be excluded from the max bundle size calculation because this - * mapping is included after the result is returned from the hotword detector service. + * 'android.service.voice.extra.PROXIMITY_VALUE' and the value of proximity value (integer) + * will be stored to enable proximity logic. {@link HotwordDetectedResult#PROXIMITY_NEAR} will + * indicate 'NEAR' proximity and {@link HotwordDetectedResult#PROXIMITY_FAR} will indicate 'FAR' + * proximity. The proximity value is provided by the system, on devices that support detecting + * proximity of nearby users, to help disambiguate which nearby device should respond. When the + * proximity is unknown, the proximity value will not be stored. This mapping will be excluded + * from the max bundle size calculation because this mapping is included after the result is + * returned from the hotword detector service. * * <p>This is a PersistableBundle so it doesn't allow any remotable objects or other contents * that can be used to communicate with other processes. @@ -1012,10 +1108,10 @@ public final class HotwordDetectedResult implements Parcelable { } @DataClass.Generated( - time = 1668466781144L, + time = 1668528946960L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/service/voice/HotwordDetectedResult.java", - inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate static final int LIMIT_HOTWORD_OFFSET_MAX_VALUE\nprivate static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE\npublic static final java.lang.String EXTRA_PROXIMITY_METERS\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull java.util.List<android.service.voice.HotwordAudioStream> mAudioStreams\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int sMaxBundleSize\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static java.util.List<android.service.voice.HotwordAudioStream> defaultAudioStreams()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static int getParcelableSize(android.os.Parcelable)\npublic static int getUsageSize(android.service.voice.HotwordDetectedResult)\nprivate static int bitCount(long)\nprivate void onConstructed()\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull java.util.List<android.service.voice.HotwordAudioStream> getAudioStreams()\npublic android.service.voice.HotwordDetectedResult.Builder buildUpon()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull android.service.voice.HotwordDetectedResult.Builder setAudioStreams(java.util.List<android.service.voice.HotwordAudioStream>)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull android.service.voice.HotwordDetectedResult.Builder setAudioStreams(java.util.List<android.service.voice.HotwordAudioStream>)\nclass BaseBuilder extends java.lang.Object implements []") + inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate static final int LIMIT_HOTWORD_OFFSET_MAX_VALUE\nprivate static final int LIMIT_AUDIO_CHANNEL_MAX_VALUE\nprivate static final java.lang.String EXTRA_PROXIMITY\npublic static final int PROXIMITY_UNKNOWN\npublic static final int PROXIMITY_NEAR\npublic static final int PROXIMITY_FAR\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull java.util.List<android.service.voice.HotwordAudioStream> mAudioStreams\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int sMaxBundleSize\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static java.util.List<android.service.voice.HotwordAudioStream> defaultAudioStreams()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static int getParcelableSize(android.os.Parcelable)\npublic static int getUsageSize(android.service.voice.HotwordDetectedResult)\nprivate static int bitCount(long)\nprivate void onConstructed()\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull java.util.List<android.service.voice.HotwordAudioStream> getAudioStreams()\npublic android.service.voice.HotwordDetectedResult.Builder buildUpon()\npublic void setProximity(double)\nprivate @android.service.voice.HotwordDetectedResult.ProximityValue int convertToProximityLevel(double)\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull android.service.voice.HotwordDetectedResult.Builder setAudioStreams(java.util.List<android.service.voice.HotwordAudioStream>)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull android.service.voice.HotwordDetectedResult.Builder setAudioStreams(java.util.List<android.service.voice.HotwordAudioStream>)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated private void __metadata() {} diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java index eac3bee8e790..302966af870a 100644 --- a/core/java/android/service/voice/HotwordDetectionService.java +++ b/core/java/android/service/voice/HotwordDetectionService.java @@ -92,7 +92,7 @@ public abstract class HotwordDetectionService extends Service { * TODO(b/247920386): Add TestApi annotation * @hide */ - public static final boolean ENABLE_PROXIMITY_RESULT = false; + public static final boolean ENABLE_PROXIMITY_RESULT = true; /** * Indicates that the updated status is successful. diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index ee8070888725..6f7d80caa147 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -19,7 +19,6 @@ package com.android.server.voiceinteraction; import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD; import static android.Manifest.permission.RECORD_AUDIO; import static android.service.attention.AttentionService.PROXIMITY_UNKNOWN; -import static android.service.voice.HotwordDetectedResult.EXTRA_PROXIMITY_METERS; import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_EXTERNAL; import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_MICROPHONE; import static android.service.voice.HotwordDetectionService.ENABLE_PROXIMITY_RESULT; @@ -193,7 +192,7 @@ final class HotwordDetectionConnection { @Nullable AttentionManagerInternal mAttentionManagerInternal = null; final AttentionManagerInternal.ProximityUpdateCallbackInternal mProximityCallbackInternal = - this::setProximityMeters; + this::setProximityValue; volatile HotwordDetectionServiceIdentity mIdentity; @@ -488,7 +487,7 @@ final class HotwordDetectionConnection { mSoftwareCallback.onError(); return; } - saveProximityMetersToBundle(result); + saveProximityValueToBundle(result); HotwordDetectedResult newResult; try { newResult = mHotwordAudioStreamManager.startCopyingAudioStreams(result); @@ -599,7 +598,7 @@ final class HotwordDetectionConnection { externalCallback.onError(CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION); return; } - saveProximityMetersToBundle(result); + saveProximityValueToBundle(result); externalCallback.onKeyphraseDetected(recognitionEvent, result); if (result != null) { Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(result) @@ -680,7 +679,7 @@ final class HotwordDetectionConnection { externalCallback.onError(CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION); return; } - saveProximityMetersToBundle(result); + saveProximityValueToBundle(result); HotwordDetectedResult newResult; try { newResult = mHotwordAudioStreamManager.startCopyingAudioStreams(result); @@ -1244,15 +1243,15 @@ final class HotwordDetectionConnection { }); } - private void saveProximityMetersToBundle(HotwordDetectedResult result) { + private void saveProximityValueToBundle(HotwordDetectedResult result) { synchronized (mLock) { if (result != null && mProximityMeters != PROXIMITY_UNKNOWN) { - result.getExtras().putDouble(EXTRA_PROXIMITY_METERS, mProximityMeters); + result.setProximity(mProximityMeters); } } } - private void setProximityMeters(double proximityMeters) { + private void setProximityValue(double proximityMeters) { synchronized (mLock) { mProximityMeters = proximityMeters; } |