Merge "Follow HotwordDetector / DetectorFailure API feedback" into udc-dev

This commit is contained in:
TreeHugger Robot
2023-03-20 00:24:00 +00:00
committed by Android (Google) Code Review
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();