From 3eb525997c04bfe367ed09c23f5eb1e8dd2aac69 Mon Sep 17 00:00:00 2001 From: Joanne Chung Date: Fri, 10 Feb 2023 19:11:08 +0800 Subject: [PATCH] Notify VIS the detector callback remote exception occurred It's possible the VIMS fails to notify client because of the IPC exception error, some unknown reason that causes the binder object may have problem to callback. We assume the system still have binders that is available to use, we try to notify the client procrss not use callback object. If the system is in a bad state that cannot get any binder, we do not have a good solution to resolve this case now. More details can be found in go/b244391070. The RemoteException for VQS if not be included in this change, we focus on the hotword part. Bug: 244391070 Test: manual. Force to call the method to trigger call path. The onError() callback is called. Change-Id: I30186d7efdb14c9ef0c7ed65bc089b89199d2e16 --- .../service/voice/AbstractDetector.java | 7 +++ .../voice/AlwaysOnHotwordDetector.java | 7 +++ .../voice/IVoiceInteractionService.aidl | 1 + .../voice/SoftwareHotwordDetector.java | 7 +++ .../voice/VoiceInteractionService.java | 32 +++++++++- .../voiceinteraction/DetectorSession.java | 61 +++++++++++++++---- .../DspTrustedHotwordDetectorSession.java | 51 ++++++++++++---- .../HotwordDetectionConnection.java | 15 +++-- ...SoftwareTrustedHotwordDetectorSession.java | 36 ++++++++--- .../VisualQueryDetectorSession.java | 7 ++- .../VoiceInteractionManagerServiceImpl.java | 14 ++++- 11 files changed, 196 insertions(+), 42 deletions(-) diff --git a/core/java/android/service/voice/AbstractDetector.java b/core/java/android/service/voice/AbstractDetector.java index 466bc0520ee55..644a2bfa70bd2 100644 --- a/core/java/android/service/voice/AbstractDetector.java +++ b/core/java/android/service/voice/AbstractDetector.java @@ -77,6 +77,13 @@ abstract class AbstractDetector implements HotwordDetector { mIsDetectorActive = new AtomicBoolean(true); } + boolean isSameToken(IBinder token) { + if (token == null) { + return false; + } + return mToken == token; + } + /** * Method to be called for the detector to ready/register itself with underlying system * services. diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 50ac1f36099ea..b1dc686059914 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -1707,6 +1707,13 @@ public class AlwaysOnHotwordDetector extends AbstractDetector { } } + void onDetectorRemoteException() { + Message.obtain(mHandler, MSG_DETECTION_ERROR, + new HotwordDetectionServiceFailure( + HotwordDetectionServiceFailure.ERROR_CODE_REMOTE_EXCEPTION, + "Detector remote exception occurs")).sendToTarget(); + } + class MyHandler extends Handler { MyHandler(@NonNull Looper looper) { super(looper); diff --git a/core/java/android/service/voice/IVoiceInteractionService.aidl b/core/java/android/service/voice/IVoiceInteractionService.aidl index 6a5460605f327..491056e802e5a 100644 --- a/core/java/android/service/voice/IVoiceInteractionService.aidl +++ b/core/java/android/service/voice/IVoiceInteractionService.aidl @@ -32,4 +32,5 @@ oneway interface IVoiceInteractionService { in IVoiceActionCheckCallback callback); void prepareToShowSession(in Bundle args, int flags); void showSessionFailed(in Bundle args); + void detectorRemoteExceptionOccurred(in IBinder token, int detectorType); } diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java index d4b6f3bf5c4c4..767fe378a30cc 100644 --- a/core/java/android/service/voice/SoftwareHotwordDetector.java +++ b/core/java/android/service/voice/SoftwareHotwordDetector.java @@ -77,6 +77,13 @@ class SoftwareHotwordDetector extends AbstractDetector { DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE); } + void onDetectorRemoteException() { + Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> + mCallback.onFailure(new HotwordDetectionServiceFailure( + HotwordDetectionServiceFailure.ERROR_CODE_REMOTE_EXCEPTION, + "Detector remote exception occurs")))); + } + @RequiresPermission(RECORD_AUDIO) @Override public boolean startRecognition() throws IllegalDetectorStateException { diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index a684e41a2a95d..fcc64b088def1 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -36,8 +36,8 @@ import android.content.Context; import android.content.Intent; import android.hardware.soundtrigger.KeyphraseEnrollmentInfo; import android.hardware.soundtrigger.SoundTrigger; -import android.media.voice.KeyphraseModelManager; import android.media.permission.Identity; +import android.media.voice.KeyphraseModelManager; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -180,6 +180,14 @@ public class VoiceInteractionService extends Service { VoiceInteractionService::onShowSessionFailed, VoiceInteractionService.this, args)); } + + @Override + public void detectorRemoteExceptionOccurred(@NonNull IBinder token, int detectorType) { + Log.d(TAG, "detectorRemoteExceptionOccurred"); + Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage( + VoiceInteractionService::onDetectorRemoteException, + VoiceInteractionService.this, token, detectorType)); + } }; IVoiceInteractionManagerService mSystemService; @@ -192,6 +200,28 @@ public class VoiceInteractionService extends Service { private final Set mActiveDetectors = new ArraySet<>(); + + private void onDetectorRemoteException(@NonNull IBinder token, int detectorType) { + Log.d(TAG, "onDetectorRemoteException for " + HotwordDetector.detectorTypeToString( + detectorType)); + mActiveDetectors.forEach(detector -> { + // TODO: handle normal detector, VQD + if (detectorType == HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP + && detector instanceof AlwaysOnHotwordDetector) { + AlwaysOnHotwordDetector alwaysOnDetector = (AlwaysOnHotwordDetector) detector; + if (alwaysOnDetector.isSameToken(token)) { + alwaysOnDetector.onDetectorRemoteException(); + } + } else if (detectorType == HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE + && detector instanceof SoftwareHotwordDetector) { + SoftwareHotwordDetector softwareDetector = (SoftwareHotwordDetector) detector; + if (softwareDetector.isSameToken(token)) { + softwareDetector.onDetectorRemoteException(); + } + } + }); + } + /** * Called when a user has activated an affordance to launch voice assist from the Keyguard. * diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java index 021823e5fc783..06fc41626e50f 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java @@ -83,6 +83,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IHotwordRecognitionStatusCallback; import com.android.internal.infra.AndroidFuture; import com.android.server.LocalServices; +import com.android.server.voiceinteraction.VoiceInteractionManagerServiceImpl.DetectorRemoteExceptionListener; import java.io.Closeable; import java.io.IOException; @@ -203,12 +204,16 @@ abstract class DetectorSession { boolean mPerformingExternalSourceHotwordDetection; @NonNull final IBinder mToken; + @NonNull DetectorRemoteExceptionListener mRemoteExceptionListener; + DetectorSession( @NonNull HotwordDetectionConnection.ServiceConnection remoteDetectionService, @NonNull Object lock, @NonNull Context context, @NonNull IBinder token, @NonNull IHotwordRecognitionStatusCallback callback, int voiceInteractionServiceUid, Identity voiceInteractorIdentity, - @NonNull ScheduledExecutorService scheduledExecutorService, boolean logging) { + @NonNull ScheduledExecutorService scheduledExecutorService, boolean logging, + @NonNull DetectorRemoteExceptionListener listener) { + mRemoteExceptionListener = listener; mRemoteDetectionService = remoteDetectionService; mLock = lock; mContext = context; @@ -237,6 +242,14 @@ abstract class DetectorSession { } } + void notifyOnDetectorRemoteException() { + Slog.d(TAG, "notifyOnDetectorRemoteException: mRemoteExceptionListener=" + + mRemoteExceptionListener); + if (mRemoteExceptionListener != null) { + mRemoteExceptionListener.onDetectorRemoteException(mToken, getDetectorType()); + } + } + @SuppressWarnings("GuardedBy") private void updateStateAfterProcessStartLocked(PersistableBundle options, SharedMemory sharedMemory) { @@ -280,6 +293,7 @@ abstract class DetectorSession { METRICS_CALLBACK_ON_STATUS_REPORTED_EXCEPTION, mVoiceInteractionServiceUid); } + notifyOnDetectorRemoteException(); } } }; @@ -319,6 +333,7 @@ abstract class DetectorSession { METRICS_CALLBACK_ON_STATUS_REPORTED_EXCEPTION, mVoiceInteractionServiceUid); } + notifyOnDetectorRemoteException(); } } else if (err != null) { Slog.w(TAG, "Failed to update state: " + err); @@ -443,6 +458,7 @@ abstract class DetectorSession { HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_ERROR_EXCEPTION, mVoiceInteractionServiceUid); } + notifyOnDetectorRemoteException(); } } finally { synchronized (mLock) { @@ -479,8 +495,12 @@ abstract class DetectorSession { EXTERNAL_HOTWORD_CLEANUP_MILLIS, TimeUnit.MILLISECONDS); - callback.onRejected(result); - + try { + callback.onRejected(result); + } catch (RemoteException e) { + notifyOnDetectorRemoteException(); + throw e; + } if (result != null) { Slog.i(TAG, "Egressed 'hotword rejected result' " + "from hotword trusted process"); @@ -516,10 +536,15 @@ abstract class DetectorSession { getDetectorType(), EXTERNAL_SOURCE_DETECT_SECURITY_EXCEPTION, mVoiceInteractionServiceUid); - callback.onError(new HotwordDetectionServiceFailure( - CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION, - "Security exception occurs in #onDetected" - + " method.")); + try { + callback.onError(new HotwordDetectionServiceFailure( + CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION, + "Security exception occurs in #onDetected" + + " method.")); + } catch (RemoteException e1) { + notifyOnDetectorRemoteException(); + throw e1; + } return; } HotwordDetectedResult newResult; @@ -530,13 +555,23 @@ abstract class DetectorSession { Slog.w(TAG, "Ignoring #onDetected due to a " + "IOException", e); // TODO: Write event - callback.onError(new HotwordDetectionServiceFailure( - CALLBACK_ONDETECTED_STREAM_COPY_ERROR, - "Copy audio stream failure.")); + try { + callback.onError(new HotwordDetectionServiceFailure( + CALLBACK_ONDETECTED_STREAM_COPY_ERROR, + "Copy audio stream failure.")); + } catch (RemoteException e1) { + notifyOnDetectorRemoteException(); + throw e1; + } return; } - callback.onDetected(newResult, /* audioFormat= */ null, - /* audioStream= */ null); + try { + callback.onDetected(newResult, /* audioFormat= */ null, + /* audioStream= */ null); + } catch (RemoteException e) { + notifyOnDetectorRemoteException(); + throw e; + } Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(newResult) + " bits from hotword trusted process"); @@ -571,6 +606,7 @@ abstract class DetectorSession { mDestroyed = true; mDebugHotwordLogging = false; mRemoteDetectionService = null; + mRemoteExceptionListener = null; if (mAttentionManagerInternal != null) { mAttentionManagerInternal.onStopProximityUpdates(mProximityCallbackInternal); } @@ -599,6 +635,7 @@ abstract class DetectorSession { HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_ERROR_EXCEPTION, mVoiceInteractionServiceUid); } + notifyOnDetectorRemoteException(); } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java index e6cb943cbb67e..e358d6be7434e 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java @@ -42,6 +42,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IHotwordRecognitionStatusCallback; +import com.android.server.voiceinteraction.VoiceInteractionManagerServiceImpl.DetectorRemoteExceptionListener; import java.io.IOException; import java.io.PrintWriter; @@ -80,10 +81,11 @@ final class DspTrustedHotwordDetectorSession extends DetectorSession { @NonNull Object lock, @NonNull Context context, @NonNull IBinder token, @NonNull IHotwordRecognitionStatusCallback callback, int voiceInteractionServiceUid, Identity voiceInteractorIdentity, - @NonNull ScheduledExecutorService scheduledExecutorService, boolean logging) { + @NonNull ScheduledExecutorService scheduledExecutorService, boolean logging, + @NonNull DetectorRemoteExceptionListener listener) { super(remoteHotwordDetectionService, lock, context, token, callback, voiceInteractionServiceUid, voiceInteractorIdentity, scheduledExecutorService, - logging); + logging, listener); } @SuppressWarnings("GuardedBy") @@ -131,9 +133,14 @@ final class DspTrustedHotwordDetectorSession extends DetectorSession { HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP, METRICS_KEYPHRASE_TRIGGERED_DETECT_SECURITY_EXCEPTION, mVoiceInteractionServiceUid); - externalCallback.onDetectionFailure(new HotwordDetectionServiceFailure( - CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION, - "Security exception occurs in #onDetected method.")); + try { + externalCallback.onDetectionFailure(new HotwordDetectionServiceFailure( + CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION, + "Security exception occurs in #onDetected method.")); + } catch (RemoteException e1) { + notifyOnDetectorRemoteException(); + throw e1; + } return; } saveProximityValueToBundle(result); @@ -141,15 +148,25 @@ final class DspTrustedHotwordDetectorSession extends DetectorSession { try { newResult = mHotwordAudioStreamCopier.startCopyingAudioStreams(result); } catch (IOException e) { - Slog.w(TAG, "Ignoring #onDetected due to a IOException", e); - externalCallback.onDetectionFailure(new HotwordDetectionServiceFailure( - CALLBACK_ONDETECTED_STREAM_COPY_ERROR, - "Copy audio stream failure.")); + try { + Slog.w(TAG, "Ignoring #onDetected due to a IOException", e); + externalCallback.onDetectionFailure(new HotwordDetectionServiceFailure( + CALLBACK_ONDETECTED_STREAM_COPY_ERROR, + "Copy audio stream failure.")); + } catch (RemoteException e1) { + notifyOnDetectorRemoteException(); + throw e1; + } return; } - externalCallback.onKeyphraseDetected(recognitionEvent, newResult); - Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(newResult) - + " bits from hotword trusted process"); + try { + externalCallback.onKeyphraseDetected(recognitionEvent, newResult); + Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(newResult) + + " bits from hotword trusted process"); + } catch (RemoteException e) { + notifyOnDetectorRemoteException(); + throw e; + } if (mDebugHotwordLogging) { Slog.i(TAG, "Egressed detected result: " + newResult); } @@ -181,7 +198,12 @@ final class DspTrustedHotwordDetectorSession extends DetectorSession { return; } mValidatingDspTrigger = false; - externalCallback.onRejected(result); + try { + externalCallback.onRejected(result); + } catch (RemoteException e) { + notifyOnDetectorRemoteException(); + throw e; + } mLastHotwordRejectedResult = result; if (mDebugHotwordLogging && result != null) { Slog.i(TAG, "Egressed rejected result: " + result); @@ -216,6 +238,7 @@ final class DspTrustedHotwordDetectorSession extends DetectorSession { HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP, HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_ERROR_EXCEPTION, mVoiceInteractionServiceUid); + notifyOnDetectorRemoteException(); } }, MAX_VALIDATION_TIMEOUT_MILLIS, @@ -248,6 +271,7 @@ final class DspTrustedHotwordDetectorSession extends DetectorSession { HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP, HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_REJECTED_EXCEPTION, mVoiceInteractionServiceUid); + notifyOnDetectorRemoteException(); } mValidatingDspTrigger = false; } @@ -261,6 +285,7 @@ final class DspTrustedHotwordDetectorSession extends DetectorSession { HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP, HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_PROCESS_RESTARTED_EXCEPTION, mVoiceInteractionServiceUid); + notifyOnDetectorRemoteException(); } mPerformingExternalSourceHotwordDetection = false; diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 1ba397529bbda..43592348adb3c 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -69,6 +69,7 @@ import com.android.internal.app.IVisualQueryDetectionAttentionListener; import com.android.internal.infra.ServiceConnector; import com.android.server.LocalServices; import com.android.server.pm.permission.PermissionManagerServiceInternal; +import com.android.server.voiceinteraction.VoiceInteractionManagerServiceImpl.DetectorRemoteExceptionListener; import java.io.PrintWriter; import java.time.Instant; @@ -147,6 +148,8 @@ final class HotwordDetectionConnection { @GuardedBy("mLock") private boolean mDebugHotwordLogging = false; + private DetectorRemoteExceptionListener mRemoteExceptionListener; + /** * For multiple detectors feature, we only support one AlwaysOnHotwordDetector and one * SoftwareHotwordDetector at the same time. We use SparseArray with detector type as the key @@ -159,7 +162,8 @@ final class HotwordDetectionConnection { HotwordDetectionConnection(Object lock, Context context, int voiceInteractionServiceUid, Identity voiceInteractorIdentity, ComponentName hotwordDetectionServiceName, ComponentName visualQueryDetectionServiceName, int userId, - boolean bindInstantServiceAllowed, int detectorType) { + boolean bindInstantServiceAllowed, int detectorType, + DetectorRemoteExceptionListener listener) { mLock = lock; mContext = context; mVoiceInteractionServiceUid = voiceInteractionServiceUid; @@ -168,6 +172,7 @@ final class HotwordDetectionConnection { mVisualQueryDetectionComponentName = visualQueryDetectionServiceName; mUser = userId; mDetectorType = detectorType; + mRemoteExceptionListener = listener; mReStartPeriodSeconds = DeviceConfig.getInt(DeviceConfig.NAMESPACE_VOICE_INTERACTION, KEY_RESTART_PERIOD_IN_SECONDS, 0); @@ -251,6 +256,7 @@ final class HotwordDetectionConnection { void cancelLocked() { Slog.v(TAG, "cancelLocked"); clearDebugHotwordLoggingTimeoutLocked(); + mRemoteExceptionListener = null; runForEachDetectorSessionLocked((session) -> { session.destroyLocked(); }); @@ -772,7 +778,8 @@ final class HotwordDetectionConnection { } session = new DspTrustedHotwordDetectorSession(mRemoteHotwordDetectionService, mLock, mContext, token, callback, mVoiceInteractionServiceUid, - mVoiceInteractorIdentity, mScheduledExecutorService, mDebugHotwordLogging); + mVoiceInteractorIdentity, mScheduledExecutorService, mDebugHotwordLogging, + mRemoteExceptionListener); } else if (detectorType == HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) { if (mRemoteVisualQueryDetectionService == null) { mRemoteVisualQueryDetectionService = @@ -781,7 +788,7 @@ final class HotwordDetectionConnection { session = new VisualQueryDetectorSession( mRemoteVisualQueryDetectionService, mLock, mContext, token, callback, mVoiceInteractionServiceUid, mVoiceInteractorIdentity, - mScheduledExecutorService, mDebugHotwordLogging); + mScheduledExecutorService, mDebugHotwordLogging, mRemoteExceptionListener); } else { if (mRemoteHotwordDetectionService == null) { mRemoteHotwordDetectionService = @@ -790,7 +797,7 @@ final class HotwordDetectionConnection { session = new SoftwareTrustedHotwordDetectorSession( mRemoteHotwordDetectionService, mLock, mContext, token, callback, mVoiceInteractionServiceUid, mVoiceInteractorIdentity, - mScheduledExecutorService, mDebugHotwordLogging); + mScheduledExecutorService, mDebugHotwordLogging, mRemoteExceptionListener); } mDetectorSessions.put(detectorType, session); session.initialize(options, sharedMemory); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java index 3f053b0fe95f9..8934c3964b211 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java @@ -43,6 +43,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IHotwordRecognitionStatusCallback; +import com.android.server.voiceinteraction.VoiceInteractionManagerServiceImpl.DetectorRemoteExceptionListener; import java.io.IOException; import java.io.PrintWriter; @@ -68,10 +69,11 @@ final class SoftwareTrustedHotwordDetectorSession extends DetectorSession { @NonNull Object lock, @NonNull Context context, @NonNull IBinder token, @NonNull IHotwordRecognitionStatusCallback callback, int voiceInteractionServiceUid, Identity voiceInteractorIdentity, - @NonNull ScheduledExecutorService scheduledExecutorService, boolean logging) { + @NonNull ScheduledExecutorService scheduledExecutorService, boolean logging, + @NonNull DetectorRemoteExceptionListener listener) { super(remoteHotwordDetectionService, lock, context, token, callback, voiceInteractionServiceUid, voiceInteractorIdentity, scheduledExecutorService, - logging); + logging, listener); } @SuppressWarnings("GuardedBy") @@ -123,9 +125,14 @@ final class SoftwareTrustedHotwordDetectorSession extends DetectorSession { HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE, METRICS_KEYPHRASE_TRIGGERED_DETECT_SECURITY_EXCEPTION, mVoiceInteractionServiceUid); - mSoftwareCallback.onError(new HotwordDetectionServiceFailure( - CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION, - "Security exception occurs in #onDetected method.")); + try { + mSoftwareCallback.onError(new HotwordDetectionServiceFailure( + CALLBACK_ONDETECTED_GOT_SECURITY_EXCEPTION, + "Security exception occurs in #onDetected method.")); + } catch (RemoteException e1) { + notifyOnDetectorRemoteException(); + throw e1; + } return; } saveProximityValueToBundle(result); @@ -135,12 +142,22 @@ final class SoftwareTrustedHotwordDetectorSession extends DetectorSession { } catch (IOException e) { Slog.w(TAG, "Ignoring #onDetected due to a IOException", e); // TODO: Write event - mSoftwareCallback.onError(new HotwordDetectionServiceFailure( - CALLBACK_ONDETECTED_STREAM_COPY_ERROR, - "Copy audio stream failure.")); + try { + mSoftwareCallback.onError(new HotwordDetectionServiceFailure( + CALLBACK_ONDETECTED_STREAM_COPY_ERROR, + "Copy audio stream failure.")); + } catch (RemoteException e1) { + notifyOnDetectorRemoteException(); + throw e1; + } return; } - mSoftwareCallback.onDetected(newResult, null, null); + try { + mSoftwareCallback.onDetected(newResult, null, null); + } catch (RemoteException e1) { + notifyOnDetectorRemoteException(); + throw e1; + } Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(newResult) + " bits from hotword trusted process"); if (mDebugHotwordLogging) { @@ -206,6 +223,7 @@ final class SoftwareTrustedHotwordDetectorSession extends DetectorSession { HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE, HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_ON_PROCESS_RESTARTED_EXCEPTION, mVoiceInteractionServiceUid); + notifyOnDetectorRemoteException(); } // Restart listening from microphone if the hotword process has been restarted. diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java index c397812b4158c..afe5dab0b78ba 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java @@ -38,6 +38,7 @@ import android.util.Slog; import com.android.internal.app.IHotwordRecognitionStatusCallback; import com.android.internal.app.IVisualQueryDetectionAttentionListener; +import com.android.server.voiceinteraction.VoiceInteractionManagerServiceImpl.DetectorRemoteExceptionListener; import java.io.PrintWriter; import java.util.Objects; @@ -64,13 +65,15 @@ final class VisualQueryDetectorSession extends DetectorSession { @NonNull Object lock, @NonNull Context context, @NonNull IBinder token, @NonNull IHotwordRecognitionStatusCallback callback, int voiceInteractionServiceUid, Identity voiceInteractorIdentity, - @NonNull ScheduledExecutorService scheduledExecutorService, boolean logging) { + @NonNull ScheduledExecutorService scheduledExecutorService, boolean logging, + @NonNull DetectorRemoteExceptionListener listener) { super(remoteService, lock, context, token, callback, voiceInteractionServiceUid, voiceInteractorIdentity, scheduledExecutorService, - logging); + logging, listener); mEgressingData = false; mQueryStreaming = false; mAttentionListener = null; + // TODO: handle notify RemoteException to client } @Override diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index 96b69f8c4130c..929e033315f78 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -743,7 +743,15 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne mHotwordDetectionConnection = new HotwordDetectionConnection(mServiceStub, mContext, mInfo.getServiceInfo().applicationInfo.uid, voiceInteractorIdentity, mHotwordDetectionComponentName, mVisualQueryDetectionComponentName, mUser, - /* bindInstantServiceAllowed= */ false, detectorType); + /* bindInstantServiceAllowed= */ false, detectorType, + (token1, detectorType1) -> { + try { + mService.detectorRemoteExceptionOccurred(token1, detectorType1); + } catch (RemoteException e) { + Slog.w(TAG, "Fail to notify client detector remote " + + "exception occurred."); + } + }); } else if (detectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) { // TODO: Logger events should be handled in session instead. Temporary adding the // checking to prevent confusion so VisualQueryDetection events won't be logged if the @@ -1080,4 +1088,8 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne // client always get the callback even if session is unexpectedly closed. mServiceStub.setSessionWindowVisible(connection.mToken, false); } + + interface DetectorRemoteExceptionListener { + void onDetectorRemoteException(@NonNull IBinder token, int detectorType); + } }