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); + } }