Follow HotwordDetector / DetectorFailure API feedback

By API Council feedback, there are two suggestions instead of
using instanceof.
(1) Use multiple methods in the callback.
(2) Use one big failure class which has a field for the failure type.

For suggestion 1, the assistant application can easy know the failure
comes from which module and take its next action in the different
callback method. Currently there are three kinds of detectors, I list
what callbacks will be used by different detectors.
(1)AlwaysOnHotwordDetector:
    onFailure(HotwordDetectionServiceFailure)
    onFailure(SoundTriggerFailure)
    onUnknownFailure(String)
(2)SoftwareHotwordDetector
    onFailure(HotwordDetectionServiceFailure)
    onUnknownFailure(String)
(3)VisualQueryDetector
    onFailure(VisualQueryDetectionServiceFailure)
    onUnknownFailure(String)

For suggestion 2, it seems that the assistant application still needs
to take the action first to know the failure comes from which module
in this one callback method.

We think that the suggestion 1 is better than suggestion 2.

Bug: 271107031
Test: atest CtsVoiceInteractionTestCases
Change-Id: I57fa93d5a0c2919ce762688f1d97766e531ccfb3
Merged-In: I57fa93d5a0c2919ce762688f1d97766e531ccfb3
This commit is contained in:
lpeter
2023-03-10 09:24:58 +08:00
parent 52cef828f1
commit 78c84e15d2
23 changed files with 528 additions and 507 deletions

View File

@@ -13033,6 +13033,7 @@ package android.service.voice {
public abstract static class AlwaysOnHotwordDetector.Callback implements android.service.voice.HotwordDetector.Callback {
ctor public AlwaysOnHotwordDetector.Callback();
method public abstract void onAvailabilityChanged(int);
method public void onFailure(@NonNull android.service.voice.SoundTriggerFailure);
method public void onHotwordDetectionServiceInitialized(int);
method public void onHotwordDetectionServiceRestarted();
method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult);
@@ -13056,17 +13057,12 @@ package android.service.voice {
method public int getStart();
}
public abstract class DetectorFailure implements android.os.Parcelable {
method public int describeContents();
method @NonNull public String getErrorMessage();
method public abstract int getSuggestedAction();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.DetectorFailure> CREATOR;
field public static final int SUGGESTED_ACTION_DISABLE_DETECTION = 2; // 0x2
field public static final int SUGGESTED_ACTION_NONE = 1; // 0x1
field public static final int SUGGESTED_ACTION_RECREATE_DETECTOR = 3; // 0x3
field public static final int SUGGESTED_ACTION_RESTART_RECOGNITION = 4; // 0x4
field public static final int SUGGESTED_ACTION_UNKNOWN = 0; // 0x0
public final class FailureSuggestedAction {
field public static final int DISABLE_DETECTION = 2; // 0x2
field public static final int NONE = 1; // 0x1
field public static final int RECREATE_DETECTOR = 3; // 0x3
field public static final int RESTART_RECOGNITION = 4; // 0x4
field public static final int UNKNOWN = 0; // 0x0
}
public final class HotwordAudioStream implements android.os.Parcelable {
@@ -13163,9 +13159,12 @@ package android.service.voice {
method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult);
}
public final class HotwordDetectionServiceFailure extends android.service.voice.DetectorFailure {
public final class HotwordDetectionServiceFailure implements android.os.Parcelable {
method public int describeContents();
method public int getErrorCode();
method @NonNull public String getErrorMessage();
method public int getSuggestedAction();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.HotwordDetectionServiceFailure> CREATOR;
field public static final int ERROR_CODE_BINDING_DIED = 2; // 0x2
field public static final int ERROR_CODE_BIND_FAILURE = 1; // 0x1
@@ -13188,12 +13187,13 @@ package android.service.voice {
public static interface HotwordDetector.Callback {
method public void onDetected(@NonNull android.service.voice.AlwaysOnHotwordDetector.EventPayload);
method @Deprecated public void onError();
method public default void onFailure(@NonNull android.service.voice.DetectorFailure);
method public default void onFailure(@NonNull android.service.voice.HotwordDetectionServiceFailure);
method public void onHotwordDetectionServiceInitialized(int);
method public void onHotwordDetectionServiceRestarted();
method public void onRecognitionPaused();
method public void onRecognitionResumed();
method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult);
method public default void onUnknownFailure(@NonNull String);
}
public final class HotwordRejectedResult implements android.os.Parcelable {
@@ -13220,9 +13220,12 @@ package android.service.voice {
field public static final int INITIALIZATION_STATUS_UNKNOWN = 100; // 0x64
}
public final class SoundTriggerFailure extends android.service.voice.DetectorFailure {
public final class SoundTriggerFailure implements android.os.Parcelable {
method public int describeContents();
method public int getErrorCode();
method @NonNull public String getErrorMessage();
method public int getSuggestedAction();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.SoundTriggerFailure> CREATOR;
field public static final int ERROR_CODE_MODULE_DIED = 1; // 0x1
field public static final int ERROR_CODE_RECOGNITION_RESUME_FAILED = 2; // 0x2
@@ -13230,11 +13233,6 @@ package android.service.voice {
field public static final int ERROR_CODE_UNKNOWN = 0; // 0x0
}
public final class UnknownFailure extends android.service.voice.DetectorFailure {
method public int getSuggestedAction();
field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.UnknownFailure> CREATOR;
}
public abstract class VisualQueryDetectionService extends android.app.Service implements android.service.voice.SandboxedDetectionInitializer {
ctor public VisualQueryDetectionService();
method public final void finishQuery() throws java.lang.IllegalStateException;
@@ -13249,9 +13247,12 @@ package android.service.voice {
field public static final String SERVICE_INTERFACE = "android.service.voice.VisualQueryDetectionService";
}
public final class VisualQueryDetectionServiceFailure extends android.service.voice.DetectorFailure {
public final class VisualQueryDetectionServiceFailure implements android.os.Parcelable {
method public int describeContents();
method public int getErrorCode();
method @NonNull public String getErrorMessage();
method public int getSuggestedAction();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.VisualQueryDetectionServiceFailure> CREATOR;
field public static final int ERROR_CODE_BINDING_DIED = 2; // 0x2
field public static final int ERROR_CODE_BIND_FAILURE = 1; // 0x1
@@ -13269,10 +13270,11 @@ package android.service.voice {
}
public static interface VisualQueryDetector.Callback {
method public void onFailure(@NonNull android.service.voice.DetectorFailure);
method public void onFailure(@NonNull android.service.voice.VisualQueryDetectionServiceFailure);
method public void onQueryDetected(@NonNull String);
method public void onQueryFinished();
method public void onQueryRejected();
method public void onUnknownFailure(@NonNull String);
method public void onVisualQueryDetectionServiceInitialized(int);
method public void onVisualQueryDetectionServiceRestarted();
}

View File

@@ -2897,23 +2897,19 @@ package android.service.voice {
field public static final boolean ENABLE_PROXIMITY_RESULT = true;
}
public final class HotwordDetectionServiceFailure extends android.service.voice.DetectorFailure {
public final class HotwordDetectionServiceFailure implements android.os.Parcelable {
ctor public HotwordDetectionServiceFailure(int, @NonNull String);
}
public final class SoundTriggerFailure extends android.service.voice.DetectorFailure {
public final class SoundTriggerFailure implements android.os.Parcelable {
ctor public SoundTriggerFailure(int, @NonNull String);
}
public final class UnknownFailure extends android.service.voice.DetectorFailure {
ctor public UnknownFailure(@NonNull String);
}
public final class VisibleActivityInfo implements android.os.Parcelable {
ctor public VisibleActivityInfo(int, @NonNull android.os.IBinder);
}
public final class VisualQueryDetectionServiceFailure extends android.service.voice.DetectorFailure {
public final class VisualQueryDetectionServiceFailure implements android.os.Parcelable {
ctor public VisualQueryDetectionServiceFailure(int, @NonNull String);
}

View File

@@ -228,11 +228,16 @@ abstract class AbstractDetector implements HotwordDetector {
/** Called when the detection fails due to an error. */
@Override
public void onError(DetectorFailure detectorFailure) {
Slog.v(TAG, "BinderCallback#onError detectorFailure: " + detectorFailure);
public void onHotwordDetectionServiceFailure(
HotwordDetectionServiceFailure hotwordDetectionServiceFailure) {
Slog.v(TAG, "BinderCallback#onHotwordDetectionServiceFailure: "
+ hotwordDetectionServiceFailure);
Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> {
mCallback.onFailure(detectorFailure != null ? detectorFailure
: new UnknownFailure("Error data is null"));
if (hotwordDetectionServiceFailure != null) {
mCallback.onFailure(hotwordDetectionServiceFailure);
} else {
mCallback.onUnknownFailure("Error data is null");
}
}));
}

View File

@@ -280,6 +280,9 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
private static final int MSG_HOTWORD_REJECTED = 6;
private static final int MSG_HOTWORD_STATUS_REPORTED = 7;
private static final int MSG_PROCESS_RESTARTED = 8;
private static final int MSG_DETECTION_HOTWORD_DETECTION_SERVICE_FAILURE = 9;
private static final int MSG_DETECTION_SOUND_TRIGGER_FAILURE = 10;
private static final int MSG_DETECTION_UNKNOWN_FAILURE = 11;
private final String mText;
private final Locale mLocale;
@@ -774,12 +777,28 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
/**
* {@inheritDoc}
*
* @deprecated Use {@link HotwordDetector.Callback#onError(DetectorFailure)} instead.
* @deprecated On {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above,
* implement {@link HotwordDetector.Callback#onFailure(HotwordDetectionServiceFailure)},
* {@link AlwaysOnHotwordDetector.Callback#onFailure(SoundTriggerFailure)},
* {@link HotwordDetector.Callback#onUnknownFailure(String)} instead.
*/
@Deprecated
@Override
public abstract void onError();
/**
* Called when the detection fails due to an error occurs in the
* {@link com.android.server.soundtrigger.SoundTriggerService} and
* {@link com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareService},
* {@link SoundTriggerFailure} will be reported to the detector.
*
* @param soundTriggerFailure It provides the error code, error message and suggested
* action.
*/
public void onFailure(@NonNull SoundTriggerFailure soundTriggerFailure) {
onError();
}
/** {@inheritDoc} */
public abstract void onRecognitionPaused();
@@ -1556,18 +1575,43 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
@Override
public void onError(int status) {
Slog.i(TAG, "onError: " + status);
// This is a workaround before the sound trigger uses the onDetectionFailure method.
Message.obtain(mHandler, MSG_DETECTION_ERROR,
new SoundTriggerFailure(status, "Sound trigger error")).sendToTarget();
// TODO(b/271534248): This is a workaround before the sound trigger uses the new error
// method.
Message.obtain(mHandler, MSG_DETECTION_SOUND_TRIGGER_FAILURE,
new SoundTriggerFailure(SoundTriggerFailure.ERROR_CODE_UNKNOWN,
"Sound trigger error")).sendToTarget();
}
@Override
public void onDetectionFailure(DetectorFailure detectorFailure) {
Slog.v(TAG, "onDetectionFailure detectorFailure: " + detectorFailure);
Message.obtain(mHandler, MSG_DETECTION_ERROR,
detectorFailure != null ? detectorFailure
: new UnknownFailure("Error data is null")).sendToTarget();
public void onHotwordDetectionServiceFailure(
HotwordDetectionServiceFailure hotwordDetectionServiceFailure) {
Slog.v(TAG, "onHotwordDetectionServiceFailure: " + hotwordDetectionServiceFailure);
if (hotwordDetectionServiceFailure != null) {
Message.obtain(mHandler, MSG_DETECTION_HOTWORD_DETECTION_SERVICE_FAILURE,
hotwordDetectionServiceFailure).sendToTarget();
} else {
Message.obtain(mHandler, MSG_DETECTION_UNKNOWN_FAILURE,
"Error data is null").sendToTarget();
}
}
@Override
public void onVisualQueryDetectionServiceFailure(
VisualQueryDetectionServiceFailure visualQueryDetectionServiceFailure)
throws RemoteException {
// It should never be called here.
Slog.w(TAG,
"onVisualQueryDetectionServiceFailure: " + visualQueryDetectionServiceFailure);
}
@Override
public void onUnknownFailure(String errorMessage) throws RemoteException {
Slog.v(TAG, "onUnknownFailure: " + errorMessage);
Message.obtain(mHandler, MSG_DETECTION_UNKNOWN_FAILURE,
!TextUtils.isEmpty(errorMessage) ? errorMessage
: "Error data is null").sendToTarget();
}
@Override
public void onRecognitionPaused() {
Slog.i(TAG, "onRecognitionPaused");
@@ -1600,7 +1644,7 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
}
void onDetectorRemoteException() {
Message.obtain(mHandler, MSG_DETECTION_ERROR,
Message.obtain(mHandler, MSG_DETECTION_HOTWORD_DETECTION_SERVICE_FAILURE,
new HotwordDetectionServiceFailure(
HotwordDetectionServiceFailure.ERROR_CODE_REMOTE_EXCEPTION,
"Detector remote exception occurs")).sendToTarget();
@@ -1630,7 +1674,9 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
mExternalCallback.onDetected((EventPayload) message.obj);
break;
case MSG_DETECTION_ERROR:
mExternalCallback.onFailure((DetectorFailure) message.obj);
// TODO(b/271534248): After reverting the workaround, this logic is still
// necessary.
mExternalCallback.onError();
break;
case MSG_DETECTION_PAUSE:
mExternalCallback.onRecognitionPaused();
@@ -1647,6 +1693,15 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
case MSG_PROCESS_RESTARTED:
mExternalCallback.onHotwordDetectionServiceRestarted();
break;
case MSG_DETECTION_HOTWORD_DETECTION_SERVICE_FAILURE:
mExternalCallback.onFailure((HotwordDetectionServiceFailure) message.obj);
break;
case MSG_DETECTION_SOUND_TRIGGER_FAILURE:
mExternalCallback.onFailure((SoundTriggerFailure) message.obj);
break;
case MSG_DETECTION_UNKNOWN_FAILURE:
mExternalCallback.onUnknownFailure((String) message.obj);
break;
default:
super.handleMessage(message);
}

View File

@@ -1,19 +0,0 @@
/*
* Copyright (C) 2023 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 android.service.voice;
parcelable DetectorFailure;

View File

@@ -1,218 +0,0 @@
/*
* Copyright (C) 2023 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 android.service.voice;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* This is used by the assistant application to know what went wrong during using the detector
* and which action the application should take. The detector can be a HotwordDector or a visual
* query detector.
*
* <p>Any class that derives this class must only add an integer value of error source type, an
* integer value of error code and a string of error message passed into the constructor. Any other
* field will not be parceled through. If the derived class has custom parceling implementation,
* this class will not be able to unpack the parcel without having access to that implementation.
*
* @hide
*/
@SuppressLint("ParcelNotFinal") // Safe because the constructor is package-private
@SystemApi
public abstract class DetectorFailure implements Parcelable {
/**
* A suggested action due to an unknown error occurs.
*/
public static final int SUGGESTED_ACTION_UNKNOWN = 0;
/**
* Indicates that an error occurs, but no action is needed for the client. The error will be
* recovered from within the framework.
*/
public static final int SUGGESTED_ACTION_NONE = 1;
/**
* Indicates that an error occurs, but no action is needed for the client due to the error can
* not be recovered. It means that the detection will not work even though the assistant
* application creates the detector again.
*
* Example: The detection service always crashes after assistant application creates the
* detector. The assistant application can stop re-creating the detector and show a suitable
* error dialog to notify the user.
*/
public static final int SUGGESTED_ACTION_DISABLE_DETECTION = 2;
/**
* Indicates that the detection service is invalid, the client needs to destroy its detector
* first and recreate its detector later.
*/
public static final int SUGGESTED_ACTION_RECREATE_DETECTOR = 3;
/**
* Indicates that the detection has stopped. The client needs to start recognition again.
*
* Example: The system server receives a Dsp trigger event.
*/
public static final int SUGGESTED_ACTION_RESTART_RECOGNITION = 4;
/**
* @hide
*/
@IntDef(prefix = {"SUGGESTED_ACTION_"}, value = {
SUGGESTED_ACTION_UNKNOWN,
SUGGESTED_ACTION_NONE,
SUGGESTED_ACTION_DISABLE_DETECTION,
SUGGESTED_ACTION_RECREATE_DETECTOR,
SUGGESTED_ACTION_RESTART_RECOGNITION
})
@Retention(RetentionPolicy.SOURCE)
public @interface SuggestedAction {}
/**
* Indicates that an error occurs from the unknown error source.
*
* @hide
*/
public static final int ERROR_SOURCE_TYPE_UNKNOWN = -1;
/**
* Indicates that an error occurs from the hotword detection.
*
* @see HotwordDetectionServiceFailure#ERROR_CODE_BIND_FAILURE
* @see HotwordDetectionServiceFailure#ERROR_CODE_BINDING_DIED
* @see HotwordDetectionServiceFailure#ERROR_CODE_COPY_AUDIO_DATA_FAILURE
* @see HotwordDetectionServiceFailure#ERROR_CODE_DETECT_TIMEOUT
* @see HotwordDetectionServiceFailure#ERROR_CODE_ON_DETECTED_SECURITY_EXCEPTION
* @see HotwordDetectionServiceFailure#ERROR_CODE_ON_DETECTED_STREAM_COPY_FAILURE
* @see HotwordDetectionServiceFailure#ERROR_CODE_REMOTE_EXCEPTION
*
* @hide
*/
public static final int ERROR_SOURCE_TYPE_HOTWORD_DETECTION = 0;
/**
* Indicates that an error occurs from the sound trigger system service
* {@link com.android.server.soundtrigger.SoundTriggerService} and
* {@link com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareService}.
*
* @see SoundTriggerFailure#ERROR_CODE_MODULE_DIED
* @see SoundTriggerFailure#ERROR_CODE_RECOGNITION_RESUME_FAILED
* @see SoundTriggerFailure#ERROR_CODE_UNEXPECTED_PREEMPTION
*
* @hide
*/
public static final int ERROR_SOURCE_TYPE_SOUND_TRIGGER = 1;
/**
* Indicates that an error occurs from {@link VisualQueryDetectionService}.
*
* @see VisualQueryDetectionServiceFailure#ERROR_CODE_BIND_FAILURE
* @see VisualQueryDetectionServiceFailure#ERROR_CODE_BINDING_DIED
* @see VisualQueryDetectionServiceFailure#ERROR_CODE_ILLEGAL_ATTENTION_STATE
* @see VisualQueryDetectionServiceFailure#ERROR_CODE_ILLEGAL_STREAMING_STATE
* @see VisualQueryDetectionServiceFailure#ERROR_CODE_REMOTE_EXCEPTION
*
* @hide
*/
public static final int ERROR_SOURCE_TYPE_VISUAL_QUERY_DETECTION = 2;
private int mErrorSourceType = ERROR_SOURCE_TYPE_UNKNOWN;
private int mErrorCode = UnknownFailure.ERROR_CODE_UNKNOWN;
private String mErrorMessage = "Unknown";
DetectorFailure(int errorSourceType, int errorCode, @NonNull String errorMessage) {
Preconditions.checkArgumentInRange(errorSourceType, ERROR_SOURCE_TYPE_UNKNOWN,
ERROR_SOURCE_TYPE_VISUAL_QUERY_DETECTION, "errorSourceType");
if (TextUtils.isEmpty(errorMessage)) {
throw new IllegalArgumentException("errorMessage is empty or null.");
}
mErrorSourceType = errorSourceType;
mErrorCode = errorCode;
mErrorMessage = errorMessage;
}
/**
* Returns the suggested action.
*/
@SuggestedAction
public abstract int getSuggestedAction();
/**
* Returns the error code.
*
* @hide
*/
public int getErrorCode() {
return mErrorCode;
}
/**
* Returns the error message.
*/
@NonNull
public String getErrorMessage() {
return mErrorMessage;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mErrorSourceType);
dest.writeInt(mErrorCode);
dest.writeString8(mErrorMessage);
}
public static final @NonNull Parcelable.Creator<DetectorFailure> CREATOR =
new Parcelable.Creator<DetectorFailure>() {
@Override
public DetectorFailure[] newArray(int size) {
return new DetectorFailure[size];
}
@Override
public DetectorFailure createFromParcel(@NonNull Parcel in) {
final int errorSourceType = in.readInt();
final int errorCode = in.readInt();
final String errorMessage = in.readString8();
switch (errorSourceType) {
case ERROR_SOURCE_TYPE_HOTWORD_DETECTION:
return new HotwordDetectionServiceFailure(errorCode, errorMessage);
case ERROR_SOURCE_TYPE_SOUND_TRIGGER:
return new SoundTriggerFailure(errorCode, errorMessage);
case ERROR_SOURCE_TYPE_VISUAL_QUERY_DETECTION:
return new VisualQueryDetectionServiceFailure(errorCode, errorMessage);
default:
return new UnknownFailure(errorMessage);
}
}
};
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2023 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 android.service.voice;
import android.annotation.IntDef;
import android.annotation.SystemApi;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* This is used by the assistant application to know which action the application should take on a
* failure callback. The detector can be a HotwordDector or a visual query detector.
*
* @hide
*/
@SystemApi
public final class FailureSuggestedAction {
/**
* A suggested action due to an unknown error occurs.
*/
public static final int UNKNOWN = 0;
/**
* Indicates that an error occurs, but no action is needed for the client. The error will be
* recovered from within the framework.
*/
public static final int NONE = 1;
/**
* Indicates that an error occurs, but no action is needed for the client due to the error can
* not be recovered. It means that the detection will not work even though the assistant
* application creates the detector again.
*/
public static final int DISABLE_DETECTION = 2;
/**
* Indicates that the detection service is invalid, the client needs to destroy its detector
* first and recreate its detector later.
*/
public static final int RECREATE_DETECTOR = 3;
/**
* Indicates that the detection has stopped. The client needs to start recognition again.
*
* Example: The system server receives a Dsp trigger event.
*/
public static final int RESTART_RECOGNITION = 4;
/**
* @hide
*/
@IntDef({UNKNOWN, NONE, DISABLE_DETECTION, RECREATE_DETECTOR, RESTART_RECOGNITION})
@Retention(RetentionPolicy.SOURCE)
public @interface FailureSuggestedActionDef {}
private FailureSuggestedAction() {}
}

View File

@@ -22,6 +22,7 @@ import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -36,7 +37,7 @@ import java.lang.annotation.RetentionPolicy;
* @hide
*/
@SystemApi
public final class HotwordDetectionServiceFailure extends DetectorFailure {
public final class HotwordDetectionServiceFailure implements Parcelable {
/**
* An error code which means an unknown error occurs.
@@ -94,12 +95,19 @@ public final class HotwordDetectionServiceFailure extends DetectorFailure {
@Retention(RetentionPolicy.SOURCE)
public @interface HotwordDetectionServiceErrorCode {}
private int mErrorCode = ERROR_CODE_UNKNOWN;
private String mErrorMessage = "Unknown";
/**
* @hide
*/
@TestApi
public HotwordDetectionServiceFailure(int errorCode, @NonNull String errorMessage) {
super(ERROR_SOURCE_TYPE_HOTWORD_DETECTION, errorCode, errorMessage);
if (TextUtils.isEmpty(errorMessage)) {
throw new IllegalArgumentException("errorMessage is empty or null.");
}
mErrorCode = errorCode;
mErrorMessage = errorMessage;
}
/**
@@ -107,22 +115,33 @@ public final class HotwordDetectionServiceFailure extends DetectorFailure {
*/
@HotwordDetectionServiceErrorCode
public int getErrorCode() {
return super.getErrorCode();
return mErrorCode;
}
@Override
/**
* Returns the error message.
*/
@NonNull
public String getErrorMessage() {
return mErrorMessage;
}
/**
* Returns the suggested action.
*/
@FailureSuggestedAction.FailureSuggestedActionDef
public int getSuggestedAction() {
switch (getErrorCode()) {
switch (mErrorCode) {
case ERROR_CODE_BIND_FAILURE:
case ERROR_CODE_BINDING_DIED:
case ERROR_CODE_REMOTE_EXCEPTION:
return SUGGESTED_ACTION_RECREATE_DETECTOR;
return FailureSuggestedAction.RECREATE_DETECTOR;
case ERROR_CODE_DETECT_TIMEOUT:
case ERROR_CODE_ON_DETECTED_SECURITY_EXCEPTION:
case ERROR_CODE_ON_DETECTED_STREAM_COPY_FAILURE:
return SUGGESTED_ACTION_RESTART_RECOGNITION;
return FailureSuggestedAction.RESTART_RECOGNITION;
default:
return SUGGESTED_ACTION_NONE;
return FailureSuggestedAction.NONE;
}
}
@@ -133,7 +152,14 @@ public final class HotwordDetectionServiceFailure extends DetectorFailure {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(mErrorCode);
dest.writeString8(mErrorMessage);
}
@Override
public String toString() {
return "HotwordDetectionServiceFailure { errorCode = " + mErrorCode + ", errorMessage = "
+ mErrorMessage + " }";
}
public static final @NonNull Parcelable.Creator<HotwordDetectionServiceFailure> CREATOR =
@@ -145,8 +171,7 @@ public final class HotwordDetectionServiceFailure extends DetectorFailure {
@Override
public HotwordDetectionServiceFailure createFromParcel(@NonNull Parcel in) {
DetectorFailure detectorFailure = DetectorFailure.CREATOR.createFromParcel(in);
return (HotwordDetectionServiceFailure) detectorFailure;
return new HotwordDetectionServiceFailure(in.readInt(), in.readString8());
}
};
}

View File

@@ -189,24 +189,34 @@ public interface HotwordDetector {
/**
* Called when the detection fails due to an error.
*
* @deprecated On Android 14 and above, implement {@link #onFailure(DetectorFailure)}
* instead.
* @deprecated On {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above,
* implement {@link HotwordDetector.Callback#onFailure(HotwordDetectionServiceFailure)},
* {@link AlwaysOnHotwordDetector.Callback#onFailure(SoundTriggerFailure)},
* {@link HotwordDetector.Callback#onUnknownFailure(String)} instead.
*/
@Deprecated
void onError();
/**
* Called when the detection fails due to an error, the subclasses of
* {@link DetectorFailure} will be reported to the detector.
* Called when the detection fails due to an error occurs in the
* {@link HotwordDetectionService}, {@link HotwordDetectionServiceFailure} will be reported
* to the detector.
*
* @see android.service.voice.HotwordDetectionServiceFailure
* @see android.service.voice.SoundTriggerFailure
* @see android.service.voice.UnknownFailure
* @see android.service.voice.VisualQueryDetectionServiceFailure
*
* @param detectorFailure It provides the error code, error message and suggested action.
* @param hotwordDetectionServiceFailure It provides the error code, error message and
* suggested action.
*/
default void onFailure(@NonNull DetectorFailure detectorFailure) {
default void onFailure(
@NonNull HotwordDetectionServiceFailure hotwordDetectionServiceFailure) {
onError();
}
/**
* Called when the detection fails due to an unknown error occurs, an error message
* will be reported to the detector.
*
* @param errorMessage It provides the error message.
*/
default void onUnknownFailure(@NonNull String errorMessage) {
onError();
}

View File

@@ -17,8 +17,8 @@
package android.service.voice;
import android.media.AudioFormat;
import android.service.voice.DetectorFailure;
import android.service.voice.HotwordDetectedResult;
import android.service.voice.HotwordDetectionServiceFailure;
import android.service.voice.HotwordRejectedResult;
/**
@@ -39,7 +39,8 @@ oneway interface IMicrophoneHotwordDetectionVoiceInteractionCallback {
/**
* Called when the detection fails due to an error.
*/
void onError(in DetectorFailure detectorFailure);
void onHotwordDetectionServiceFailure(
in HotwordDetectionServiceFailure hotwordDetectionServiceFailure);
/**
* Called when the detected result was not detected.

View File

@@ -16,7 +16,7 @@
package android.service.voice;
import android.service.voice.DetectorFailure;
import android.service.voice.VisualQueryDetectionServiceFailure;
/**
* Callback for returning the detected result from the VisualQueryDetectionService.
@@ -43,5 +43,6 @@ oneway interface IVisualQueryDetectionVoiceInteractionCallback {
/**
* Called when the detection fails due to an error.
*/
void onDetectionFailure(in DetectorFailure detectorFailure);
void onVisualQueryDetectionServiceFailure(
in VisualQueryDetectionServiceFailure visualQueryDetectionServiceFailure);
}

View File

@@ -30,6 +30,7 @@ import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SharedMemory;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
@@ -176,11 +177,16 @@ class SoftwareHotwordDetector extends AbstractDetector {
/** Called when the detection fails due to an error. */
@Override
public void onError(DetectorFailure detectorFailure) {
Slog.v(TAG, "BinderCallback#onError detectorFailure: " + detectorFailure);
public void onHotwordDetectionServiceFailure(
HotwordDetectionServiceFailure hotwordDetectionServiceFailure) {
Slog.v(TAG, "BinderCallback#onHotwordDetectionServiceFailure:"
+ hotwordDetectionServiceFailure);
Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> {
mCallback.onFailure(detectorFailure != null ? detectorFailure
: new UnknownFailure("Error data is null"));
if (hotwordDetectionServiceFailure != null) {
mCallback.onFailure(hotwordDetectionServiceFailure);
} else {
mCallback.onUnknownFailure("Error data is null");
}
}));
}
@@ -236,11 +242,34 @@ class SoftwareHotwordDetector extends AbstractDetector {
}
@Override
public void onDetectionFailure(DetectorFailure detectorFailure) throws RemoteException {
Slog.v(TAG, "onDetectionFailure detectorFailure: " + detectorFailure);
public void onHotwordDetectionServiceFailure(
HotwordDetectionServiceFailure hotwordDetectionServiceFailure)
throws RemoteException {
Slog.v(TAG, "onHotwordDetectionServiceFailure: " + hotwordDetectionServiceFailure);
Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> {
mCallback.onFailure(detectorFailure != null ? detectorFailure
: new UnknownFailure("Error data is null"));
if (hotwordDetectionServiceFailure != null) {
mCallback.onFailure(hotwordDetectionServiceFailure);
} else {
mCallback.onUnknownFailure("Error data is null");
}
}));
}
@Override
public void onVisualQueryDetectionServiceFailure(
VisualQueryDetectionServiceFailure visualQueryDetectionServiceFailure)
throws RemoteException {
// It should never be called here.
Slog.w(TAG, "onVisualQueryDetectionServiceFailure: "
+ visualQueryDetectionServiceFailure);
}
@Override
public void onUnknownFailure(String errorMessage) throws RemoteException {
Slog.v(TAG, "onUnknownFailure: " + errorMessage);
Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> {
mCallback.onUnknownFailure(
!TextUtils.isEmpty(errorMessage) ? errorMessage : "Error data is null");
}));
}

View File

@@ -22,6 +22,7 @@ import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -35,7 +36,7 @@ import java.lang.annotation.RetentionPolicy;
* @hide
*/
@SystemApi
public final class SoundTriggerFailure extends DetectorFailure {
public final class SoundTriggerFailure implements Parcelable {
/**
* An error code which means an unknown error occurs.
@@ -72,12 +73,19 @@ public final class SoundTriggerFailure extends DetectorFailure {
@Retention(RetentionPolicy.SOURCE)
public @interface SoundTriggerErrorCode {}
private int mErrorCode = ERROR_CODE_UNKNOWN;
private String mErrorMessage = "Unknown";
/**
* @hide
*/
@TestApi
public SoundTriggerFailure(int errorCode, @NonNull String errorMessage) {
super(ERROR_SOURCE_TYPE_SOUND_TRIGGER, errorCode, errorMessage);
if (TextUtils.isEmpty(errorMessage)) {
throw new IllegalArgumentException("errorMessage is empty or null.");
}
mErrorCode = errorCode;
mErrorMessage = errorMessage;
}
/**
@@ -85,19 +93,30 @@ public final class SoundTriggerFailure extends DetectorFailure {
*/
@SoundTriggerErrorCode
public int getErrorCode() {
return super.getErrorCode();
return mErrorCode;
}
@Override
/**
* Returns the error message.
*/
@NonNull
public String getErrorMessage() {
return mErrorMessage;
}
/**
* Returns the suggested action.
*/
@FailureSuggestedAction.FailureSuggestedActionDef
public int getSuggestedAction() {
switch (getErrorCode()) {
switch (mErrorCode) {
case ERROR_CODE_MODULE_DIED:
case ERROR_CODE_UNEXPECTED_PREEMPTION:
return SUGGESTED_ACTION_RECREATE_DETECTOR;
return FailureSuggestedAction.RECREATE_DETECTOR;
case ERROR_CODE_RECOGNITION_RESUME_FAILED:
return SUGGESTED_ACTION_RESTART_RECOGNITION;
return FailureSuggestedAction.RESTART_RECOGNITION;
default:
return SUGGESTED_ACTION_NONE;
return FailureSuggestedAction.NONE;
}
}
@@ -108,7 +127,14 @@ public final class SoundTriggerFailure extends DetectorFailure {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(mErrorCode);
dest.writeString8(mErrorMessage);
}
@Override
public String toString() {
return "SoundTriggerFailure { errorCode = " + mErrorCode + ", errorMessage = "
+ mErrorMessage + " }";
}
public static final @NonNull Parcelable.Creator<SoundTriggerFailure> CREATOR =
@@ -120,7 +146,7 @@ public final class SoundTriggerFailure extends DetectorFailure {
@Override
public SoundTriggerFailure createFromParcel(@NonNull Parcel in) {
return (SoundTriggerFailure) DetectorFailure.CREATOR.createFromParcel(in);
return new SoundTriggerFailure(in.readInt(), in.readString8());
}
};
}

View File

@@ -1,19 +0,0 @@
/*
* Copyright (C) 2023 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 android.service.voice;
parcelable UnknownFailure;

View File

@@ -1,77 +0,0 @@
/*
* Copyright (C) 2023 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 android.service.voice;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
/**
* A class which indicates an unknown error occurs during the detector doing detection. The class
* is mainly used by the assistant application, the application still can get the suggested action
* for the unknown error.
*
* @hide
*/
@SystemApi
public final class UnknownFailure extends DetectorFailure {
/**
* An error code which means an unknown error occurs.
*
* @hide
*/
public static final int ERROR_CODE_UNKNOWN = 0;
/**
* @hide
*/
@TestApi
public UnknownFailure(@NonNull String errorMessage) {
super(ERROR_SOURCE_TYPE_UNKNOWN, ERROR_CODE_UNKNOWN, errorMessage);
}
@Override
public int getSuggestedAction() {
return SUGGESTED_ACTION_UNKNOWN;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
}
public static final @NonNull Parcelable.Creator<UnknownFailure> CREATOR =
new Parcelable.Creator<UnknownFailure>() {
@Override
public UnknownFailure[] newArray(int size) {
return new UnknownFailure[size];
}
@Override
public UnknownFailure createFromParcel(@NonNull Parcel in) {
return (UnknownFailure) DetectorFailure.CREATOR.createFromParcel(in);
}
};
}

View File

@@ -22,6 +22,7 @@ import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -33,7 +34,7 @@ import java.lang.annotation.RetentionPolicy;
* @hide
*/
@SystemApi
public final class VisualQueryDetectionServiceFailure extends DetectorFailure {
public final class VisualQueryDetectionServiceFailure implements Parcelable {
/**
* An error code which means an unknown error occurs.
@@ -79,12 +80,19 @@ public final class VisualQueryDetectionServiceFailure extends DetectorFailure {
@Retention(RetentionPolicy.SOURCE)
public @interface VisualQueryDetectionServiceErrorCode {}
private int mErrorCode = ERROR_CODE_UNKNOWN;
private String mErrorMessage = "Unknown";
/**
* @hide
*/
@TestApi
public VisualQueryDetectionServiceFailure(int errorCode, @NonNull String errorMessage) {
super(ERROR_SOURCE_TYPE_VISUAL_QUERY_DETECTION, errorCode, errorMessage);
if (TextUtils.isEmpty(errorMessage)) {
throw new IllegalArgumentException("errorMessage is empty or null.");
}
mErrorCode = errorCode;
mErrorMessage = errorMessage;
}
/**
@@ -92,21 +100,32 @@ public final class VisualQueryDetectionServiceFailure extends DetectorFailure {
*/
@VisualQueryDetectionServiceErrorCode
public int getErrorCode() {
return super.getErrorCode();
return mErrorCode;
}
@Override
/**
* Returns the error message.
*/
@NonNull
public String getErrorMessage() {
return mErrorMessage;
}
/**
* Returns the suggested action.
*/
@FailureSuggestedAction.FailureSuggestedActionDef
public int getSuggestedAction() {
switch (getErrorCode()) {
switch (mErrorCode) {
case ERROR_CODE_BIND_FAILURE:
case ERROR_CODE_BINDING_DIED:
case ERROR_CODE_ILLEGAL_ATTENTION_STATE:
case ERROR_CODE_REMOTE_EXCEPTION:
return SUGGESTED_ACTION_RECREATE_DETECTOR;
return FailureSuggestedAction.RECREATE_DETECTOR;
case ERROR_CODE_ILLEGAL_STREAMING_STATE:
return SUGGESTED_ACTION_RESTART_RECOGNITION;
return FailureSuggestedAction.RESTART_RECOGNITION;
default:
return SUGGESTED_ACTION_NONE;
return FailureSuggestedAction.NONE;
}
}
@@ -117,7 +136,14 @@ public final class VisualQueryDetectionServiceFailure extends DetectorFailure {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(mErrorCode);
dest.writeString8(mErrorMessage);
}
@Override
public String toString() {
return "VisualQueryDetectionServiceFailure { errorCode = " + mErrorCode
+ ", errorMessage = " + mErrorMessage + " }";
}
public static final @NonNull Parcelable.Creator<VisualQueryDetectionServiceFailure> CREATOR =
@@ -129,8 +155,7 @@ public final class VisualQueryDetectionServiceFailure extends DetectorFailure {
@Override
public VisualQueryDetectionServiceFailure createFromParcel(@NonNull Parcel in) {
DetectorFailure detectorFailure = DetectorFailure.CREATOR.createFromParcel(in);
return (VisualQueryDetectionServiceFailure) detectorFailure;
return new VisualQueryDetectionServiceFailure(in.readInt(), in.readString8());
}
};
}

View File

@@ -32,6 +32,7 @@ import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SharedMemory;
import android.text.TextUtils;
import android.util.Slog;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
@@ -216,9 +217,23 @@ public class VisualQueryDetector {
void onVisualQueryDetectionServiceRestarted();
/**
* Called when the detection fails due to an error.
* Called when the detection fails due to an error occurs in the
* {@link VisualQueryDetectionService}, {@link VisualQueryDetectionServiceFailure} will be
* reported to the detector.
*
* @param visualQueryDetectionServiceFailure It provides the error code, error message and
* suggested action.
*/
void onFailure(@NonNull DetectorFailure detectorFailure);
void onFailure(
@NonNull VisualQueryDetectionServiceFailure visualQueryDetectionServiceFailure);
/**
* Called when the detection fails due to an unknown error occurs, an error message
* will be reported to the detector.
*
* @param errorMessage It provides the error message.
*/
void onUnknownFailure(@NonNull String errorMessage);
}
private class VisualQueryDetectorInitializationDelegate extends AbstractDetector {
@@ -295,10 +310,17 @@ public class VisualQueryDetector {
/** Called when the detection fails due to an error. */
@Override
public void onDetectionFailure(DetectorFailure detectorFailure) {
Slog.v(TAG, "BinderCallback#onDetectionFailure");
Binder.withCleanCallingIdentity(() -> mExecutor.execute(
() -> mCallback.onFailure(detectorFailure)));
public void onVisualQueryDetectionServiceFailure(
VisualQueryDetectionServiceFailure visualQueryDetectionServiceFailure) {
Slog.v(TAG, "BinderCallback#onVisualQueryDetectionServiceFailure: "
+ visualQueryDetectionServiceFailure);
Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> {
if (visualQueryDetectionServiceFailure != null) {
mCallback.onFailure(visualQueryDetectionServiceFailure);
} else {
mCallback.onUnknownFailure("Error data is null");
}
}));
}
}
@@ -375,7 +397,35 @@ public class VisualQueryDetector {
}
@Override
public void onDetectionFailure(DetectorFailure detectorFailure) throws RemoteException {
public void onHotwordDetectionServiceFailure(
HotwordDetectionServiceFailure hotwordDetectionServiceFailure)
throws RemoteException {
// It should never be called here.
Slog.w(TAG, "onHotwordDetectionServiceFailure: " + hotwordDetectionServiceFailure);
}
@Override
public void onVisualQueryDetectionServiceFailure(
VisualQueryDetectionServiceFailure visualQueryDetectionServiceFailure)
throws RemoteException {
Slog.v(TAG, "onVisualQueryDetectionServiceFailure: "
+ visualQueryDetectionServiceFailure);
Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> {
if (visualQueryDetectionServiceFailure != null) {
mCallback.onFailure(visualQueryDetectionServiceFailure);
} else {
mCallback.onUnknownFailure("Error data is null");
}
}));
}
@Override
public void onUnknownFailure(String errorMessage) throws RemoteException {
Slog.v(TAG, "onUnknownFailure: " + errorMessage);
Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> {
mCallback.onUnknownFailure(
!TextUtils.isEmpty(errorMessage) ? errorMessage : "Error data is null");
}));
}
}
}

View File

@@ -17,9 +17,10 @@
package com.android.internal.app;
import android.hardware.soundtrigger.SoundTrigger;
import android.service.voice.DetectorFailure;
import android.service.voice.HotwordDetectedResult;
import android.service.voice.HotwordDetectionServiceFailure;
import android.service.voice.HotwordRejectedResult;
import android.service.voice.VisualQueryDetectionServiceFailure;
/**
* @hide
@@ -63,11 +64,31 @@ oneway interface IHotwordRecognitionStatusCallback {
void onError(int status);
/**
* Called when the detection fails due to an error.
* Called when the detection fails due to an error occurs in the
* {@link HotwordDetectionService}.
*
* @param detectorFailure It provides the error code, error message and suggested action.
* @param hotwordDetectionServiceFailure It provides the error code, error message and
* suggested action.
*/
void onDetectionFailure(in DetectorFailure detectorFailure);
void onHotwordDetectionServiceFailure(
in HotwordDetectionServiceFailure hotwordDetectionServiceFailure);
/**
* Called when the detection fails due to an error occurs in the
* {@link VisualQueryDetectionService}.
*
* @param visualQueryDetectionServiceFailure It provides the error code, error message and
* suggested action.
*/
void onVisualQueryDetectionServiceFailure(
in VisualQueryDetectionServiceFailure visualQueryDetectionServiceFailure);
/**
* Called when the detection fails due to an unknown error occurs.
*
* @param errorMessage It provides the error message.
*/
void onUnknownFailure(in String errorMessage);
/**
* Called when the recognition is paused temporarily for some reason.

View File

@@ -26,6 +26,7 @@ import static android.service.voice.HotwordDetectionService.ENABLE_PROXIMITY_RES
import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_SUCCESS;
import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_UNKNOWN;
import static android.service.voice.HotwordDetectionService.KEY_INITIALIZATION_STATUS;
import static android.service.voice.HotwordDetectionServiceFailure.ERROR_CODE_COPY_AUDIO_DATA_FAILURE;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_ERROR;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_SUCCESS;
@@ -68,7 +69,6 @@ import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SharedMemory;
import android.service.voice.DetectorFailure;
import android.service.voice.HotwordDetectedResult;
import android.service.voice.HotwordDetectionService;
import android.service.voice.HotwordDetectionServiceFailure;
@@ -76,6 +76,7 @@ import android.service.voice.HotwordDetector;
import android.service.voice.HotwordRejectedResult;
import android.service.voice.IDspHotwordDetectionCallback;
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.service.voice.VisualQueryDetectionServiceFailure;
import android.text.TextUtils;
import android.util.Pair;
import android.util.Slog;
@@ -124,17 +125,12 @@ abstract class DetectorSession {
private static final String HOTWORD_DETECTION_OP_MESSAGE =
"Providing hotword detection result to VoiceInteractionService";
// The error codes are used for onError callback
static final int HOTWORD_DETECTION_SERVICE_DIED =
HotwordDetectionServiceFailure.ERROR_CODE_BINDING_DIED;
static final int CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION =
// The error codes are used for onHotwordDetectionServiceFailure callback.
// Define these due to lines longer than 100 characters.
static final int ONDETECTED_GOT_SECURITY_EXCEPTION =
HotwordDetectionServiceFailure.ERROR_CODE_ON_DETECTED_SECURITY_EXCEPTION;
static final int CALLBACK_DETECT_TIMEOUT =
HotwordDetectionServiceFailure.ERROR_CODE_DETECT_TIMEOUT;
static final int CALLBACK_ONDETECTED_STREAM_COPY_ERROR =
static final int ONDETECTED_STREAM_COPY_ERROR =
HotwordDetectionServiceFailure.ERROR_CODE_ON_DETECTED_STREAM_COPY_FAILURE;
static final int CALLBACK_COPY_AUDIO_DATA_FAILURE =
HotwordDetectionServiceFailure.ERROR_CODE_COPY_AUDIO_DATA_FAILURE;
// TODO: These constants need to be refined.
private static final long MAX_UPDATE_TIMEOUT_MILLIS = 30000;
@@ -449,11 +445,11 @@ abstract class DetectorSession {
Slog.w(TAG, "Failed supplying audio data to validator", e);
try {
callback.onError(
new HotwordDetectionServiceFailure(CALLBACK_COPY_AUDIO_DATA_FAILURE,
callback.onHotwordDetectionServiceFailure(
new HotwordDetectionServiceFailure(ERROR_CODE_COPY_AUDIO_DATA_FAILURE,
"Copy audio data failure for external source detection."));
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to report onError status: " + ex);
Slog.w(TAG, "Failed to report onHotwordDetectionServiceFailure status: " + ex);
if (getDetectorType() != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) {
HotwordMetricsLogger.writeDetectorEvent(getDetectorType(),
HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_ERROR_EXCEPTION,
@@ -538,10 +534,11 @@ abstract class DetectorSession {
EXTERNAL_SOURCE_DETECT_SECURITY_EXCEPTION,
mVoiceInteractionServiceUid);
try {
callback.onError(new HotwordDetectionServiceFailure(
CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION,
"Security exception occurs in #onDetected"
+ " method."));
callback.onHotwordDetectionServiceFailure(
new HotwordDetectionServiceFailure(
ONDETECTED_GOT_SECURITY_EXCEPTION,
"Security exception occurs in "
+ "#onDetected method"));
} catch (RemoteException e1) {
notifyOnDetectorRemoteException();
throw e1;
@@ -557,9 +554,10 @@ abstract class DetectorSession {
+ "IOException", e);
// TODO: Write event
try {
callback.onError(new HotwordDetectionServiceFailure(
CALLBACK_ONDETECTED_STREAM_COPY_ERROR,
"Copy audio stream failure."));
callback.onHotwordDetectionServiceFailure(
new HotwordDetectionServiceFailure(
ONDETECTED_STREAM_COPY_ERROR,
"Copy audio stream failure."));
} catch (RemoteException e1) {
notifyOnDetectorRemoteException();
throw e1;
@@ -624,17 +622,40 @@ abstract class DetectorSession {
mRemoteDetectionService = remoteDetectionService;
}
void reportErrorLocked(@NonNull DetectorFailure detectorFailure) {
private void reportErrorGetRemoteException() {
if (getDetectorType() != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) {
HotwordMetricsLogger.writeDetectorEvent(getDetectorType(),
HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_ERROR_EXCEPTION,
mVoiceInteractionServiceUid);
}
notifyOnDetectorRemoteException();
}
void reportErrorLocked(@NonNull HotwordDetectionServiceFailure hotwordDetectionServiceFailure) {
try {
mCallback.onDetectionFailure(detectorFailure);
mCallback.onHotwordDetectionServiceFailure(hotwordDetectionServiceFailure);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to report onError status: " + e);
if (getDetectorType() != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) {
HotwordMetricsLogger.writeDetectorEvent(getDetectorType(),
HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_ERROR_EXCEPTION,
mVoiceInteractionServiceUid);
}
notifyOnDetectorRemoteException();
Slog.w(TAG, "Failed to call onHotwordDetectionServiceFailure: " + e);
reportErrorGetRemoteException();
}
}
void reportErrorLocked(
@NonNull VisualQueryDetectionServiceFailure visualQueryDetectionServiceFailure) {
try {
mCallback.onVisualQueryDetectionServiceFailure(visualQueryDetectionServiceFailure);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call onVisualQueryDetectionServiceFailure: " + e);
reportErrorGetRemoteException();
}
}
void reportErrorLocked(@NonNull String errorMessage) {
try {
mCallback.onUnknownFailure(errorMessage);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call onUnknownFailure: " + e);
reportErrorGetRemoteException();
}
}

View File

@@ -16,6 +16,10 @@
package com.android.server.voiceinteraction;
import static android.service.voice.HotwordDetectionServiceFailure.ERROR_CODE_DETECT_TIMEOUT;
import static android.service.voice.HotwordDetectionServiceFailure.ERROR_CODE_ON_DETECTED_SECURITY_EXCEPTION;
import static android.service.voice.HotwordDetectionServiceFailure.ERROR_CODE_ON_DETECTED_STREAM_COPY_FAILURE;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_DETECTED_EXCEPTION;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_ERROR_EXCEPTION;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_PROCESS_RESTARTED_EXCEPTION;
@@ -135,9 +139,10 @@ final class DspTrustedHotwordDetectorSession extends DetectorSession {
METRICS_KEYPHRASE_TRIGGERED_DETECT_SECURITY_EXCEPTION,
mVoiceInteractionServiceUid);
try {
externalCallback.onDetectionFailure(new HotwordDetectionServiceFailure(
CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION,
"Security exception occurs in #onDetected method."));
externalCallback.onHotwordDetectionServiceFailure(
new HotwordDetectionServiceFailure(
ERROR_CODE_ON_DETECTED_SECURITY_EXCEPTION,
"Security exception occurs in #onDetected method."));
} catch (RemoteException e1) {
notifyOnDetectorRemoteException();
HotwordMetricsLogger.writeDetectorEvent(
@@ -155,9 +160,10 @@ final class DspTrustedHotwordDetectorSession extends DetectorSession {
} catch (IOException e) {
try {
Slog.w(TAG, "Ignoring #onDetected due to a IOException", e);
externalCallback.onDetectionFailure(new HotwordDetectionServiceFailure(
CALLBACK_ONDETECTED_STREAM_COPY_ERROR,
"Copy audio stream failure."));
externalCallback.onHotwordDetectionServiceFailure(
new HotwordDetectionServiceFailure(
ERROR_CODE_ON_DETECTED_STREAM_COPY_FAILURE,
"Copy audio stream failure."));
} catch (RemoteException e1) {
notifyOnDetectorRemoteException();
throw e1;
@@ -242,8 +248,8 @@ final class DspTrustedHotwordDetectorSession extends DetectorSession {
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_TIMEOUT,
mVoiceInteractionServiceUid);
try {
externalCallback.onDetectionFailure(
new HotwordDetectionServiceFailure(CALLBACK_DETECT_TIMEOUT,
externalCallback.onHotwordDetectionServiceFailure(
new HotwordDetectionServiceFailure(ERROR_CODE_DETECT_TIMEOUT,
"Timeout to response to the detection result."));
} catch (RemoteException e) {
Slog.w(TAG, "Failed to report onError status: ", e);

View File

@@ -56,7 +56,6 @@ import android.service.voice.HotwordDetector;
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.service.voice.ISandboxedDetectionService;
import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback;
import android.service.voice.UnknownFailure;
import android.service.voice.VisualQueryDetectionService;
import android.service.voice.VisualQueryDetectionServiceFailure;
import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity;
@@ -786,8 +785,8 @@ final class HotwordDetectionConnection {
VisualQueryDetectionServiceFailure.ERROR_CODE_BINDING_DIED,
"Detection service is dead."));
} else {
detectorSession.reportErrorLocked(new UnknownFailure(
"Detection service is dead with unknown detection service type."));
detectorSession.reportErrorLocked(
"Detection service is dead with unknown detection service type.");
}
}
@@ -804,8 +803,8 @@ final class HotwordDetectionConnection {
VisualQueryDetectionServiceFailure.ERROR_CODE_BIND_FAILURE,
"Bind detection service failure."));
} else {
detectorSession.reportErrorLocked(new UnknownFailure(
"Bind detection service failure with unknown detection service type."));
detectorSession.reportErrorLocked(
"Bind detection service failure with unknown detection service type.");
}
}
}

View File

@@ -17,6 +17,8 @@
package com.android.server.voiceinteraction;
import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_MICROPHONE;
import static android.service.voice.HotwordDetectionServiceFailure.ERROR_CODE_ON_DETECTED_SECURITY_EXCEPTION;
import static android.service.voice.HotwordDetectionServiceFailure.ERROR_CODE_ON_DETECTED_STREAM_COPY_FAILURE;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_DETECTED_EXCEPTION;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_ERROR_EXCEPTION;
@@ -128,9 +130,10 @@ final class SoftwareTrustedHotwordDetectorSession extends DetectorSession {
METRICS_KEYPHRASE_TRIGGERED_DETECT_SECURITY_EXCEPTION,
mVoiceInteractionServiceUid);
try {
mSoftwareCallback.onError(new HotwordDetectionServiceFailure(
CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION,
"Security exception occurs in #onDetected method."));
mSoftwareCallback.onHotwordDetectionServiceFailure(
new HotwordDetectionServiceFailure(
ERROR_CODE_ON_DETECTED_SECURITY_EXCEPTION,
"Security exception occurs in #onDetected method."));
} catch (RemoteException e1) {
notifyOnDetectorRemoteException();
HotwordMetricsLogger.writeDetectorEvent(
@@ -149,9 +152,10 @@ final class SoftwareTrustedHotwordDetectorSession extends DetectorSession {
Slog.w(TAG, "Ignoring #onDetected due to a IOException", e);
// TODO: Write event
try {
mSoftwareCallback.onError(new HotwordDetectionServiceFailure(
CALLBACK_ONDETECTED_STREAM_COPY_ERROR,
"Copy audio stream failure."));
mSoftwareCallback.onHotwordDetectionServiceFailure(
new HotwordDetectionServiceFailure(
ERROR_CODE_ON_DETECTED_STREAM_COPY_FAILURE,
"Copy audio stream failure."));
} catch (RemoteException e1) {
notifyOnDetectorRemoteException();
HotwordMetricsLogger.writeDetectorEvent(

View File

@@ -110,11 +110,12 @@ final class VisualQueryDetectorSession extends DetectorSession {
} catch (RemoteException e) {
Slog.e(TAG, "Error delivering attention gained event.", e);
try {
callback.onDetectionFailure(new VisualQueryDetectionServiceFailure(
ERROR_CODE_ILLEGAL_ATTENTION_STATE,
"Attention listener failed to switch to GAINED state."));
callback.onVisualQueryDetectionServiceFailure(
new VisualQueryDetectionServiceFailure(
ERROR_CODE_ILLEGAL_ATTENTION_STATE,
"Attention listener failed to switch to GAINED state."));
} catch (RemoteException ex) {
Slog.v(TAG, "Fail to call onDetectionFailure");
Slog.v(TAG, "Fail to call onVisualQueryDetectionServiceFailure");
}
return;
}
@@ -132,11 +133,12 @@ final class VisualQueryDetectorSession extends DetectorSession {
} catch (RemoteException e) {
Slog.e(TAG, "Error delivering attention lost event.", e);
try {
callback.onDetectionFailure(new VisualQueryDetectionServiceFailure(
ERROR_CODE_ILLEGAL_ATTENTION_STATE,
"Attention listener failed to switch to LOST state."));
callback.onVisualQueryDetectionServiceFailure(
new VisualQueryDetectionServiceFailure(
ERROR_CODE_ILLEGAL_ATTENTION_STATE,
"Attention listener failed to switch to LOST state."));
} catch (RemoteException ex) {
Slog.v(TAG, "Fail to call onDetectionFailure");
Slog.v(TAG, "Fail to call onVisualQueryDetectionServiceFailure");
}
return;
}
@@ -148,9 +150,10 @@ final class VisualQueryDetectorSession extends DetectorSession {
Slog.v(TAG, "BinderCallback#onQueryDetected");
if (!mEgressingData) {
Slog.v(TAG, "Query should not be egressed within the unattention state.");
callback.onDetectionFailure(new VisualQueryDetectionServiceFailure(
ERROR_CODE_ILLEGAL_STREAMING_STATE,
"Cannot stream queries without attention signals."));
callback.onVisualQueryDetectionServiceFailure(
new VisualQueryDetectionServiceFailure(
ERROR_CODE_ILLEGAL_STREAMING_STATE,
"Cannot stream queries without attention signals."));
return;
}
mQueryStreaming = true;
@@ -164,9 +167,10 @@ final class VisualQueryDetectorSession extends DetectorSession {
if (!mQueryStreaming) {
Slog.v(TAG, "Query streaming state signal FINISHED is block since there is"
+ " no active query being streamed.");
callback.onDetectionFailure(new VisualQueryDetectionServiceFailure(
ERROR_CODE_ILLEGAL_STREAMING_STATE,
"Cannot send FINISHED signal with no query streamed."));
callback.onVisualQueryDetectionServiceFailure(
new VisualQueryDetectionServiceFailure(
ERROR_CODE_ILLEGAL_STREAMING_STATE,
"Cannot send FINISHED signal with no query streamed."));
return;
}
callback.onQueryFinished();
@@ -179,9 +183,10 @@ final class VisualQueryDetectorSession extends DetectorSession {
if (!mQueryStreaming) {
Slog.v(TAG, "Query streaming state signal REJECTED is block since there is"
+ " no active query being streamed.");
callback.onDetectionFailure(new VisualQueryDetectionServiceFailure(
ERROR_CODE_ILLEGAL_STREAMING_STATE,
"Cannot send REJECTED signal with no query streamed."));
callback.onVisualQueryDetectionServiceFailure(
new VisualQueryDetectionServiceFailure(
ERROR_CODE_ILLEGAL_STREAMING_STATE,
"Cannot send REJECTED signal with no query streamed."));
return;
}
callback.onQueryRejected();