Trusted Hotword: Allow disabling periodic restarts.

Mitigates quality risk that may arise due to mismanaged state around the
restarts (either on the platform or application side).

The duration will be flag-controlled in a follow-up cl so it can
actually be disabled by flag.

Bug: 224618257
Test: manual
Change-Id: I3b17b47fab4120b42d75443b213be3c1ef5491aa
(cherry picked from commit b8cd0a4286)
Merged-in: I3b17b47fab4120b42d75443b213be3c1ef5491aa
This commit is contained in:
Ahaan Ugale
2022-03-14 22:51:10 -07:00
parent 0c19f57c37
commit fc651fe0d2

View File

@@ -115,6 +115,11 @@ final class HotwordDetectionConnection {
private static final Duration MAX_UPDATE_TIMEOUT_DURATION = private static final Duration MAX_UPDATE_TIMEOUT_DURATION =
Duration.ofMillis(MAX_UPDATE_TIMEOUT_MILLIS); Duration.ofMillis(MAX_UPDATE_TIMEOUT_MILLIS);
private static final long RESET_DEBUG_HOTWORD_LOGGING_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour private static final long RESET_DEBUG_HOTWORD_LOGGING_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour
/**
* Time after which each HotwordDetectionService process is stopped and replaced by a new one.
* 0 indicates no restarts.
*/
private static final int RESTART_PERIOD_SECONDS = 3600; // 60 minutes
private static final int MAX_ISOLATED_PROCESS_NUMBER = 10; private static final int MAX_ISOLATED_PROCESS_NUMBER = 10;
// Hotword metrics // Hotword metrics
@@ -133,6 +138,7 @@ final class HotwordDetectionConnection {
// TODO: This may need to be a Handler(looper) // TODO: This may need to be a Handler(looper)
private final ScheduledExecutorService mScheduledExecutorService = private final ScheduledExecutorService mScheduledExecutorService =
Executors.newSingleThreadScheduledExecutor(); Executors.newSingleThreadScheduledExecutor();
@Nullable private final ScheduledFuture<?> mCancellationTaskFuture;
private final AtomicBoolean mUpdateStateAfterStartFinished = new AtomicBoolean(false); private final AtomicBoolean mUpdateStateAfterStartFinished = new AtomicBoolean(false);
private final IBinder.DeathRecipient mAudioServerDeathRecipient = this::audioServerDied; private final IBinder.DeathRecipient mAudioServerDeathRecipient = this::audioServerDied;
private final @NonNull ServiceConnectionFactory mServiceConnectionFactory; private final @NonNull ServiceConnectionFactory mServiceConnectionFactory;
@@ -148,7 +154,6 @@ final class HotwordDetectionConnection {
private IMicrophoneHotwordDetectionVoiceInteractionCallback mSoftwareCallback; private IMicrophoneHotwordDetectionVoiceInteractionCallback mSoftwareCallback;
private Instant mLastRestartInstant; private Instant mLastRestartInstant;
private ScheduledFuture<?> mCancellationTaskFuture;
private ScheduledFuture<?> mCancellationKeyPhraseDetectionFuture; private ScheduledFuture<?> mCancellationKeyPhraseDetectionFuture;
private ScheduledFuture<?> mDebugHotwordLoggingTimeoutFuture = null; private ScheduledFuture<?> mDebugHotwordLoggingTimeoutFuture = null;
@@ -194,16 +199,20 @@ final class HotwordDetectionConnection {
mLastRestartInstant = Instant.now(); mLastRestartInstant = Instant.now();
updateStateAfterProcessStart(options, sharedMemory); updateStateAfterProcessStart(options, sharedMemory);
// TODO(volnov): we need to be smarter here, e.g. schedule it a bit more often, but wait if (RESTART_PERIOD_SECONDS <= 0) {
// until the current session is closed. mCancellationTaskFuture = null;
mCancellationTaskFuture = mScheduledExecutorService.scheduleAtFixedRate(() -> { } else {
Slog.v(TAG, "Time to restart the process, TTL has passed"); // TODO(volnov): we need to be smarter here, e.g. schedule it a bit more often, but wait
synchronized (mLock) { // until the current session is closed.
restartProcessLocked(); mCancellationTaskFuture = mScheduledExecutorService.scheduleAtFixedRate(() -> {
HotwordMetricsLogger.writeServiceRestartEvent(mDetectorType, Slog.v(TAG, "Time to restart the process, TTL has passed");
HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__SCHEDULE); synchronized (mLock) {
} restartProcessLocked();
}, 30, 30, TimeUnit.MINUTES); HotwordMetricsLogger.writeServiceRestartEvent(mDetectorType,
HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__SCHEDULE);
}
}, RESTART_PERIOD_SECONDS, RESTART_PERIOD_SECONDS, TimeUnit.SECONDS);
}
} }
private void initAudioFlingerLocked() { private void initAudioFlingerLocked() {
@@ -341,7 +350,9 @@ final class HotwordDetectionConnection {
.setHotwordDetectionServiceProvider(null); .setHotwordDetectionServiceProvider(null);
mIdentity = null; mIdentity = null;
updateServiceUidForAudioPolicy(Process.INVALID_UID); updateServiceUidForAudioPolicy(Process.INVALID_UID);
mCancellationTaskFuture.cancel(/* may interrupt */ true); if (mCancellationTaskFuture != null) {
mCancellationTaskFuture.cancel(/* may interrupt */ true);
}
if (mAudioFlinger != null) { if (mAudioFlinger != null) {
mAudioFlinger.unlinkToDeath(mAudioServerDeathRecipient, /* flags= */ 0); mAudioFlinger.unlinkToDeath(mAudioServerDeathRecipient, /* flags= */ 0);
} }
@@ -759,6 +770,7 @@ final class HotwordDetectionConnection {
} }
public void dump(String prefix, PrintWriter pw) { public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("RESTART_PERIOD_SECONDS="); pw.println(RESTART_PERIOD_SECONDS);
pw.print(prefix); pw.print(prefix);
pw.print("mBound=" + mRemoteHotwordDetectionService.isBound()); pw.print("mBound=" + mRemoteHotwordDetectionService.isBound());
pw.print(", mValidatingDspTrigger=" + mValidatingDspTrigger); pw.print(", mValidatingDspTrigger=" + mValidatingDspTrigger);