diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 50e58f57dfc23..6177a9f0500f9 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -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 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 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 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 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 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(); } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index b856bc2ce853c..b7d97eae56aae 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -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); } diff --git a/core/java/android/service/voice/AbstractDetector.java b/core/java/android/service/voice/AbstractDetector.java index 0f3e8d1f4bdee..8648e386fbbbe 100644 --- a/core/java/android/service/voice/AbstractDetector.java +++ b/core/java/android/service/voice/AbstractDetector.java @@ -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"); + } })); } diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index e69e03e1fac3b..8688a18880b7c 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -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); } diff --git a/core/java/android/service/voice/DetectorFailure.aidl b/core/java/android/service/voice/DetectorFailure.aidl deleted file mode 100644 index 3591329ac1114..0000000000000 --- a/core/java/android/service/voice/DetectorFailure.aidl +++ /dev/null @@ -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; diff --git a/core/java/android/service/voice/DetectorFailure.java b/core/java/android/service/voice/DetectorFailure.java deleted file mode 100644 index c6efdc3063e36..0000000000000 --- a/core/java/android/service/voice/DetectorFailure.java +++ /dev/null @@ -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. - * - *

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 CREATOR = - new Parcelable.Creator() { - @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); - } - } - }; -} diff --git a/core/java/android/service/voice/FailureSuggestedAction.java b/core/java/android/service/voice/FailureSuggestedAction.java new file mode 100644 index 0000000000000..8408c3ba953a8 --- /dev/null +++ b/core/java/android/service/voice/FailureSuggestedAction.java @@ -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() {} +} diff --git a/core/java/android/service/voice/HotwordDetectionServiceFailure.java b/core/java/android/service/voice/HotwordDetectionServiceFailure.java index 3d9f66b173d4e..5cf245d8624b5 100644 --- a/core/java/android/service/voice/HotwordDetectionServiceFailure.java +++ b/core/java/android/service/voice/HotwordDetectionServiceFailure.java @@ -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 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()); } }; } diff --git a/core/java/android/service/voice/HotwordDetector.java b/core/java/android/service/voice/HotwordDetector.java index 0c8fd48a39d2c..32a93eef7fda6 100644 --- a/core/java/android/service/voice/HotwordDetector.java +++ b/core/java/android/service/voice/HotwordDetector.java @@ -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(); } diff --git a/core/java/android/service/voice/IMicrophoneHotwordDetectionVoiceInteractionCallback.aidl b/core/java/android/service/voice/IMicrophoneHotwordDetectionVoiceInteractionCallback.aidl index f800c1ee22d63..fab830af9d481 100644 --- a/core/java/android/service/voice/IMicrophoneHotwordDetectionVoiceInteractionCallback.aidl +++ b/core/java/android/service/voice/IMicrophoneHotwordDetectionVoiceInteractionCallback.aidl @@ -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. diff --git a/core/java/android/service/voice/IVisualQueryDetectionVoiceInteractionCallback.aidl b/core/java/android/service/voice/IVisualQueryDetectionVoiceInteractionCallback.aidl index 1a935c0acbf69..cedb7ff624970 100644 --- a/core/java/android/service/voice/IVisualQueryDetectionVoiceInteractionCallback.aidl +++ b/core/java/android/service/voice/IVisualQueryDetectionVoiceInteractionCallback.aidl @@ -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); } diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java index 77900d7bcb5a0..eac7aee43859e 100644 --- a/core/java/android/service/voice/SoftwareHotwordDetector.java +++ b/core/java/android/service/voice/SoftwareHotwordDetector.java @@ -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"); })); } diff --git a/core/java/android/service/voice/SoundTriggerFailure.java b/core/java/android/service/voice/SoundTriggerFailure.java index a431fbc0fe1dd..5560800a373f3 100644 --- a/core/java/android/service/voice/SoundTriggerFailure.java +++ b/core/java/android/service/voice/SoundTriggerFailure.java @@ -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 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()); } }; } diff --git a/core/java/android/service/voice/UnknownFailure.aidl b/core/java/android/service/voice/UnknownFailure.aidl deleted file mode 100644 index cf43cc2fad46c..0000000000000 --- a/core/java/android/service/voice/UnknownFailure.aidl +++ /dev/null @@ -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; diff --git a/core/java/android/service/voice/UnknownFailure.java b/core/java/android/service/voice/UnknownFailure.java deleted file mode 100644 index 2ef2d5f880fd0..0000000000000 --- a/core/java/android/service/voice/UnknownFailure.java +++ /dev/null @@ -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 CREATOR = - new Parcelable.Creator() { - @Override - public UnknownFailure[] newArray(int size) { - return new UnknownFailure[size]; - } - - @Override - public UnknownFailure createFromParcel(@NonNull Parcel in) { - return (UnknownFailure) DetectorFailure.CREATOR.createFromParcel(in); - } - }; -} diff --git a/core/java/android/service/voice/VisualQueryDetectionServiceFailure.java b/core/java/android/service/voice/VisualQueryDetectionServiceFailure.java index aa02299b2b287..4657e37486bf0 100644 --- a/core/java/android/service/voice/VisualQueryDetectionServiceFailure.java +++ b/core/java/android/service/voice/VisualQueryDetectionServiceFailure.java @@ -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 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()); } }; } diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java index d7bf07498715c..b4f5ff1046ae6 100644 --- a/core/java/android/service/voice/VisualQueryDetector.java +++ b/core/java/android/service/voice/VisualQueryDetector.java @@ -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"); + })); } } } diff --git a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl index 813febf33964b..ad0d1a4019916 100644 --- a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl +++ b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl @@ -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. diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java index dbc824c384a29..cd29dace2263f 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java @@ -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(); } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java index f9b5111f53487..9a4fbdc4516a9 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java @@ -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); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 4fd5979b3d9f0..3eabea67e8906 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -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."); } } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java index 367fb8166b00b..f06c99729a19e 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java @@ -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( diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java index afe5dab0b78ba..aadb38da54697 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java @@ -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();