Implement timeout mechanism when HotwordDetectionService initializes
Bug: 183684347 Test: atest CtsVoiceInteractionTestCases Test: atest CtsVoiceInteractionTestCases --instant Change-Id: I77d29491b055ee2a45aa088df4c1cd54e604c259
This commit is contained in:
@@ -28,8 +28,10 @@ import android.annotation.SystemApi;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioFormat;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.IRemoteCallback;
|
||||
import android.os.Looper;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.PersistableBundle;
|
||||
@@ -37,8 +39,6 @@ import android.os.RemoteException;
|
||||
import android.os.SharedMemory;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.app.IHotwordRecognitionStatusCallback;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -58,6 +58,8 @@ public abstract class HotwordDetectionService extends Service {
|
||||
private static final boolean DBG = true;
|
||||
|
||||
private static final long UPDATE_TIMEOUT_MILLIS = 5000;
|
||||
/** @hide */
|
||||
public static final String KEY_INITIALIZATION_STATUS = "initialization_status";
|
||||
|
||||
/** @hide */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@@ -141,7 +143,7 @@ public abstract class HotwordDetectionService extends Service {
|
||||
|
||||
@Override
|
||||
public void updateState(PersistableBundle options, SharedMemory sharedMemory,
|
||||
IHotwordRecognitionStatusCallback callback) throws RemoteException {
|
||||
IRemoteCallback callback) throws RemoteException {
|
||||
if (DBG) {
|
||||
Log.d(TAG, "#updateState");
|
||||
}
|
||||
@@ -314,14 +316,15 @@ public abstract class HotwordDetectionService extends Service {
|
||||
}
|
||||
|
||||
private void onUpdateStateInternal(@Nullable PersistableBundle options,
|
||||
@Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) {
|
||||
// TODO (b/183684347): Implement timeout case.
|
||||
@Nullable SharedMemory sharedMemory, IRemoteCallback callback) {
|
||||
IntConsumer intConsumer = null;
|
||||
if (callback != null) {
|
||||
intConsumer =
|
||||
value -> {
|
||||
try {
|
||||
callback.onStatusReported(value);
|
||||
Bundle status = new Bundle();
|
||||
status.putInt(KEY_INITIALIZATION_STATUS, value);
|
||||
callback.sendResult(status);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
@@ -17,13 +17,12 @@
|
||||
package android.service.voice;
|
||||
|
||||
import android.media.AudioFormat;
|
||||
import android.os.IRemoteCallback;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.PersistableBundle;
|
||||
import android.os.SharedMemory;
|
||||
import android.service.voice.IDspHotwordDetectionCallback;
|
||||
|
||||
import com.android.internal.app.IHotwordRecognitionStatusCallback;
|
||||
|
||||
/**
|
||||
* Provide the interface to communicate with hotword detection service.
|
||||
*
|
||||
@@ -44,5 +43,5 @@ oneway interface IHotwordDetectionService {
|
||||
in IDspHotwordDetectionCallback callback);
|
||||
|
||||
void updateState(in PersistableBundle options, in SharedMemory sharedMemory,
|
||||
in IHotwordRecognitionStatusCallback callback);
|
||||
in IRemoteCallback callback);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.android.server.voiceinteraction;
|
||||
|
||||
import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_EXTERNAL;
|
||||
import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_MICROPHONE;
|
||||
import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_UNKNOWN;
|
||||
import static android.service.voice.HotwordDetectionService.KEY_INITIALIZATION_STATUS;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
@@ -31,6 +33,8 @@ import android.media.AudioFormat;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioRecord;
|
||||
import android.media.MediaRecorder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IRemoteCallback;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.PersistableBundle;
|
||||
import android.os.RemoteException;
|
||||
@@ -46,6 +50,7 @@ import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.app.IHotwordRecognitionStatusCallback;
|
||||
import com.android.internal.infra.AndroidFuture;
|
||||
import com.android.internal.infra.ServiceConnector;
|
||||
|
||||
import java.io.Closeable;
|
||||
@@ -58,6 +63,8 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* A class that provides the communication with the HotwordDetectionService.
|
||||
@@ -75,11 +82,13 @@ final class HotwordDetectionConnection {
|
||||
private static final int MAX_STREAMING_SECONDS = 10;
|
||||
private static final int MICROPHONE_BUFFER_LENGTH_SECONDS = 8;
|
||||
private static final int HOTWORD_AUDIO_LENGTH_SECONDS = 3;
|
||||
private static final long MAX_UPDATE_TIMEOUT_MILLIS = 6000;
|
||||
|
||||
private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool();
|
||||
// TODO: This may need to be a Handler(looper)
|
||||
private final ScheduledExecutorService mScheduledExecutorService =
|
||||
Executors.newSingleThreadScheduledExecutor();
|
||||
private final AtomicBoolean mUpdateStateFinish = new AtomicBoolean(false);
|
||||
|
||||
final Object mLock;
|
||||
final ComponentName mDetectionComponentName;
|
||||
@@ -107,16 +116,11 @@ final class HotwordDetectionConnection {
|
||||
@Override // from ServiceConnector.Impl
|
||||
protected void onServiceConnectionStatusChanged(IHotwordDetectionService service,
|
||||
boolean connected) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "onServiceConnectionStatusChanged connected = " + connected);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mBound = connected;
|
||||
if (connected) {
|
||||
try {
|
||||
service.updateState(options, sharedMemory, callback);
|
||||
} catch (RemoteException e) {
|
||||
// TODO: (b/181842909) Report an error to voice interactor
|
||||
Slog.w(TAG, "Failed to updateState for HotwordDetectionService", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +130,67 @@ final class HotwordDetectionConnection {
|
||||
}
|
||||
};
|
||||
mRemoteHotwordDetectionService.connect();
|
||||
if (callback == null) {
|
||||
updateStateLocked(options, sharedMemory);
|
||||
return;
|
||||
}
|
||||
updateStateWithCallbackLocked(options, sharedMemory, callback);
|
||||
}
|
||||
|
||||
private void updateStateWithCallbackLocked(PersistableBundle options,
|
||||
SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "updateStateWithCallbackLocked");
|
||||
}
|
||||
mRemoteHotwordDetectionService.postAsync(service -> {
|
||||
AndroidFuture<Void> future = new AndroidFuture<>();
|
||||
IRemoteCallback statusCallback = new IRemoteCallback.Stub() {
|
||||
@Override
|
||||
public void sendResult(Bundle bundle) throws RemoteException {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "updateState finish");
|
||||
}
|
||||
future.complete(null);
|
||||
try {
|
||||
if (mUpdateStateFinish.getAndSet(true)) {
|
||||
Slog.w(TAG, "call callback after timeout");
|
||||
return;
|
||||
}
|
||||
int status = bundle != null ? bundle.getInt(
|
||||
KEY_INITIALIZATION_STATUS,
|
||||
INITIALIZATION_STATUS_UNKNOWN)
|
||||
: INITIALIZATION_STATUS_UNKNOWN;
|
||||
callback.onStatusReported(status);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Failed to report initialization status: " + e);
|
||||
}
|
||||
}
|
||||
};
|
||||
try {
|
||||
service.updateState(options, sharedMemory, statusCallback);
|
||||
} catch (RemoteException e) {
|
||||
// TODO: (b/181842909) Report an error to voice interactor
|
||||
Slog.w(TAG, "Failed to updateState for HotwordDetectionService", e);
|
||||
}
|
||||
return future;
|
||||
}).orTimeout(MAX_UPDATE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
|
||||
.whenComplete((res, err) -> {
|
||||
if (err instanceof TimeoutException) {
|
||||
Slog.w(TAG, "updateState timed out");
|
||||
try {
|
||||
if (mUpdateStateFinish.getAndSet(true)) {
|
||||
return;
|
||||
}
|
||||
callback.onStatusReported(INITIALIZATION_STATUS_UNKNOWN);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Failed to report initialization status: " + e);
|
||||
}
|
||||
} else if (err != null) {
|
||||
Slog.w(TAG, "Failed to update state: " + err);
|
||||
} else {
|
||||
// NOTE: so far we don't need to take any action.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isBound() {
|
||||
|
||||
Reference in New Issue
Block a user