Merge "send onFailure callback for asyncronous exceptions" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
c3e906d1cf
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 =
|
||||
|
||||
Reference in New Issue
Block a user