Merge "send onFailure callback for asyncronous exceptions" into udc-dev

This commit is contained in:
Treehugger Robot
2023-05-11 17:17:34 +00:00
committed by Android (Google) Code Review
2 changed files with 73 additions and 16 deletions

View File

@@ -18,6 +18,7 @@ package android.service.voice;
import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
import static android.Manifest.permission.RECORD_AUDIO;
import static android.service.voice.SoundTriggerFailure.ERROR_CODE_UNKNOWN;
import static android.service.voice.VoiceInteractionService.MULTIPLE_ACTIVE_HOTWORD_DETECTORS;
import android.annotation.ElapsedRealtimeLong;
@@ -269,6 +270,15 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
static final long THROW_ON_INITIALIZE_IF_NO_DSP = 269165460L;
/**
* Gates returning {@link Callback#onFailure} and {@link Callback#onUnknownFailure}
* when asynchronous exceptions are propagated to the client. If the change is not enabled,
* the existing behavior of delivering {@link #STATE_ERROR} is retained.
*/
@ChangeId
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
static final long SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS = 280471513L;
/**
* Controls the sensitivity threshold adjustment factor for a given model.
* Negative value corresponds to less sensitive model (high threshold) and
@@ -1409,12 +1419,16 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
if (mAvailability == STATE_KEYPHRASE_ENROLLED) {
try {
stopRecognitionLocked();
} catch (SecurityException e) {
Slog.w(TAG, "Failed to Stop the recognition", e);
if (mTargetSdkVersion <= Build.VERSION_CODES.R) {
throw e;
} catch (Exception e) {
Slog.w(TAG, "Failed to stop recognition after enrollment update", e);
if (CompatChanges.isChangeEnabled(SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS)) {
sendSoundTriggerFailure(new SoundTriggerFailure(ERROR_CODE_UNKNOWN,
"Failed to stop recognition after enrollment update: "
+ Log.getStackTraceString(e),
FailureSuggestedAction.RECREATE_DETECTOR));
} else {
updateAndNotifyStateChangedLocked(STATE_ERROR);
}
updateAndNotifyStateChangedLocked(STATE_ERROR);
return;
}
}
@@ -1538,6 +1552,12 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
@GuardedBy("mLock")
private void updateAndNotifyStateChangedLocked(int availability) {
updateAvailabilityLocked(availability);
notifyStateChangedLocked();
}
@GuardedBy("mLock")
private void updateAvailabilityLocked(int availability) {
if (DBG) {
Slog.d(TAG, "Hotword availability changed from " + mAvailability
+ " -> " + availability);
@@ -1545,7 +1565,6 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
if (!mIsAvailabilityOverriddenByTestApi) {
mAvailability = availability;
}
notifyStateChangedLocked();
}
@GuardedBy("mLock")
@@ -1555,6 +1574,18 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
message.sendToTarget();
}
@GuardedBy("mLock")
private void sendUnknownFailure(String failureMessage) {
// update but do not call onAvailabilityChanged callback for STATE_ERROR
updateAvailabilityLocked(STATE_ERROR);
Message.obtain(mHandler, MSG_DETECTION_UNKNOWN_FAILURE, failureMessage).sendToTarget();
}
private void sendSoundTriggerFailure(@NonNull SoundTriggerFailure soundTriggerFailure) {
Message.obtain(mHandler, MSG_DETECTION_SOUND_TRIGGER_FAILURE, soundTriggerFailure)
.sendToTarget();
}
/** @hide */
static final class SoundTriggerListener extends IHotwordRecognitionStatusCallback.Stub {
private final Handler mHandler;
@@ -1726,6 +1757,7 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
}
}
// TODO(b/267681692): remove the AsyncTask usage
class RefreshAvailabilityTask extends AsyncTask<Void, Void, Void> {
@Override
@@ -1744,13 +1776,17 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
}
updateAndNotifyStateChangedLocked(availability);
}
} catch (SecurityException e) {
} catch (Exception e) {
// Any exception here not caught will crash the process because AsyncTask does not
// bubble up the exceptions to the client app, so we must propagate it to the app.
Slog.w(TAG, "Failed to refresh availability", e);
if (mTargetSdkVersion <= Build.VERSION_CODES.R) {
throw e;
}
synchronized (mLock) {
updateAndNotifyStateChangedLocked(STATE_ERROR);
if (CompatChanges.isChangeEnabled(SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS)) {
sendUnknownFailure(
"Failed to refresh availability: " + Log.getStackTraceString(e));
} else {
updateAndNotifyStateChangedLocked(STATE_ERROR);
}
}
}

View File

@@ -74,14 +74,22 @@ public final class SoundTriggerFailure implements Parcelable {
public @interface SoundTriggerErrorCode {}
private final int mErrorCode;
private final int mSuggestedAction;
private final String mErrorMessage;
/**
* @hide
*/
@TestApi
public SoundTriggerFailure(@SoundTriggerErrorCode int errorCode,
@NonNull String errorMessage) {
public SoundTriggerFailure(@SoundTriggerErrorCode int errorCode, @NonNull String errorMessage) {
this(errorCode, errorMessage, getSuggestedActionBasedOnErrorCode(errorCode));
}
/**
* @hide
*/
public SoundTriggerFailure(@SoundTriggerErrorCode int errorCode, @NonNull String errorMessage,
@FailureSuggestedAction.FailureSuggestedActionDef int suggestedAction) {
if (TextUtils.isEmpty(errorMessage)) {
throw new IllegalArgumentException("errorMessage is empty or null.");
}
@@ -95,7 +103,13 @@ public final class SoundTriggerFailure implements Parcelable {
default:
throw new IllegalArgumentException("Invalid ErrorCode: " + errorCode);
}
if (suggestedAction != getSuggestedActionBasedOnErrorCode(errorCode)
&& errorCode != ERROR_CODE_UNKNOWN) {
throw new IllegalArgumentException("Invalid suggested next action: "
+ "errorCode=" + errorCode + ", suggestedAction=" + suggestedAction);
}
mErrorMessage = errorMessage;
mSuggestedAction = suggestedAction;
}
/**
@@ -119,7 +133,11 @@ public final class SoundTriggerFailure implements Parcelable {
*/
@FailureSuggestedAction.FailureSuggestedActionDef
public int getSuggestedAction() {
switch (mErrorCode) {
return mSuggestedAction;
}
private static int getSuggestedActionBasedOnErrorCode(@SoundTriggerErrorCode int errorCode) {
switch (errorCode) {
case ERROR_CODE_UNKNOWN:
case ERROR_CODE_MODULE_DIED:
case ERROR_CODE_UNEXPECTED_PREEMPTION:
@@ -144,8 +162,11 @@ public final class SoundTriggerFailure implements Parcelable {
@Override
public String toString() {
return "SoundTriggerFailure { errorCode = " + mErrorCode + ", errorMessage = "
+ mErrorMessage + " }";
return "SoundTriggerFailure {"
+ " errorCode = " + mErrorCode
+ ", errorMessage = " + mErrorMessage
+ ", suggestedNextAction = " + mSuggestedAction
+ " }";
}
public static final @NonNull Parcelable.Creator<SoundTriggerFailure> CREATOR =