Merge changes from topic "presubmit-am-08bbcb587d4e47b6afcc900c23899344" into sc-v2-dev

* changes:
  Add hotword detection metrics.
  Add HotwordMetricsLogger for statistics logging.
This commit is contained in:
Joanne Chung
2022-03-18 10:02:36 +00:00
committed by Android (Google) Code Review
9 changed files with 390 additions and 31 deletions

View File

@@ -44,14 +44,17 @@ abstract class AbstractHotwordDetector implements HotwordDetector {
private final IVoiceInteractionManagerService mManagerService; private final IVoiceInteractionManagerService mManagerService;
private final Handler mHandler; private final Handler mHandler;
private final HotwordDetector.Callback mCallback; private final HotwordDetector.Callback mCallback;
private final int mDetectorType;
AbstractHotwordDetector( AbstractHotwordDetector(
IVoiceInteractionManagerService managerService, IVoiceInteractionManagerService managerService,
HotwordDetector.Callback callback) { HotwordDetector.Callback callback,
int detectorType) {
mManagerService = managerService; mManagerService = managerService;
// TODO: this needs to be supplied from above // TODO: this needs to be supplied from above
mHandler = new Handler(Looper.getMainLooper()); mHandler = new Handler(Looper.getMainLooper());
mCallback = callback; mCallback = callback;
mDetectorType = detectorType;
} }
/** /**
@@ -104,19 +107,20 @@ abstract class AbstractHotwordDetector implements HotwordDetector {
Slog.d(TAG, "updateState()"); Slog.d(TAG, "updateState()");
} }
synchronized (mLock) { synchronized (mLock) {
updateStateLocked(options, sharedMemory, null /* callback */); updateStateLocked(options, sharedMemory, null /* callback */, mDetectorType);
} }
} }
protected void updateStateLocked(@Nullable PersistableBundle options, protected void updateStateLocked(@Nullable PersistableBundle options,
@Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) { @Nullable SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback,
int detectorType) {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "updateStateLocked()"); Slog.d(TAG, "updateStateLocked()");
} }
Identity identity = new Identity(); Identity identity = new Identity();
identity.packageName = ActivityThread.currentOpPackageName(); identity.packageName = ActivityThread.currentOpPackageName();
try { try {
mManagerService.updateState(identity, options, sharedMemory, callback); mManagerService.updateState(identity, options, sharedMemory, callback, detectorType);
} catch (RemoteException e) { } catch (RemoteException e) {
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
} }

View File

@@ -578,7 +578,9 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
IVoiceInteractionManagerService modelManagementService, int targetSdkVersion, IVoiceInteractionManagerService modelManagementService, int targetSdkVersion,
boolean supportHotwordDetectionService, @Nullable PersistableBundle options, boolean supportHotwordDetectionService, @Nullable PersistableBundle options,
@Nullable SharedMemory sharedMemory) { @Nullable SharedMemory sharedMemory) {
super(modelManagementService, callback); super(modelManagementService, callback,
supportHotwordDetectionService ? DETECTOR_TYPE_TRUSTED_HOTWORD_DSP
: DETECTOR_TYPE_NORMAL);
mHandler = new MyHandler(); mHandler = new MyHandler();
mText = text; mText = text;
@@ -590,7 +592,8 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector {
mTargetSdkVersion = targetSdkVersion; mTargetSdkVersion = targetSdkVersion;
mSupportHotwordDetectionService = supportHotwordDetectionService; mSupportHotwordDetectionService = supportHotwordDetectionService;
if (mSupportHotwordDetectionService) { if (mSupportHotwordDetectionService) {
updateStateLocked(options, sharedMemory, mInternalCallback); updateStateLocked(options, sharedMemory, mInternalCallback,
DETECTOR_TYPE_TRUSTED_HOTWORD_DSP);
} }
try { try {
Identity identity = new Identity(); Identity identity = new Identity();

View File

@@ -36,6 +36,27 @@ import android.os.SharedMemory;
@SystemApi @SystemApi
public interface HotwordDetector { public interface HotwordDetector {
/**
* Indicates that it is a non-trusted hotword detector.
*
* @hide
*/
int DETECTOR_TYPE_NORMAL = 0;
/**
* Indicates that it is a DSP trusted hotword detector.
*
* @hide
*/
int DETECTOR_TYPE_TRUSTED_HOTWORD_DSP = 1;
/**
* Indicates that it is a software trusted hotword detector.
*
* @hide
*/
int DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE = 2;
/** /**
* Starts hotword recognition. * Starts hotword recognition.
* <p> * <p>
@@ -97,6 +118,22 @@ public interface HotwordDetector {
*/ */
void updateState(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory); void updateState(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory);
/**
* @hide
*/
static String detectorTypeToString(int detectorType) {
switch (detectorType) {
case DETECTOR_TYPE_NORMAL:
return "normal";
case DETECTOR_TYPE_TRUSTED_HOTWORD_DSP:
return "trusted_hotword_dsp";
case DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE:
return "trusted_hotword_software";
default:
return Integer.toString(detectorType);
}
}
/** /**
* The callback to notify of detection events. * The callback to notify of detection events.
*/ */

View File

@@ -60,14 +60,15 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector {
PersistableBundle options, PersistableBundle options,
SharedMemory sharedMemory, SharedMemory sharedMemory,
HotwordDetector.Callback callback) { HotwordDetector.Callback callback) {
super(managerService, callback); super(managerService, callback, DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE);
mManagerService = managerService; mManagerService = managerService;
mAudioFormat = audioFormat; mAudioFormat = audioFormat;
mCallback = callback; mCallback = callback;
mHandler = new Handler(Looper.getMainLooper()); mHandler = new Handler(Looper.getMainLooper());
updateStateLocked(options, sharedMemory, updateStateLocked(options, sharedMemory,
new InitializationStateListener(mHandler, mCallback)); new InitializationStateListener(mHandler, mCallback),
DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE);
} }
@RequiresPermission(RECORD_AUDIO) @RequiresPermission(RECORD_AUDIO)

View File

@@ -242,12 +242,14 @@ interface IVoiceInteractionManagerService {
* {@link HotwordDetectionService}. Use this to provide the hotword models data or other * {@link HotwordDetectionService}. Use this to provide the hotword models data or other
* such data to the trusted process. * such data to the trusted process.
* @param callback Use this to report {@link HotwordDetectionService} status. * @param callback Use this to report {@link HotwordDetectionService} status.
* @param detectorType Indicate which detector is used.
*/ */
void updateState( void updateState(
in Identity originatorIdentity, in Identity originatorIdentity,
in PersistableBundle options, in PersistableBundle options,
in SharedMemory sharedMemory, in SharedMemory sharedMemory,
in IHotwordRecognitionStatusCallback callback); in IHotwordRecognitionStatusCallback callback,
int detectorType);
/** /**
* Requests to shutdown hotword detection service. * Requests to shutdown hotword detection service.

View File

@@ -20,9 +20,30 @@ import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
import static android.Manifest.permission.RECORD_AUDIO; import static android.Manifest.permission.RECORD_AUDIO;
import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_EXTERNAL; import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_EXTERNAL;
import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_MICROPHONE; import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_MICROPHONE;
import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_SUCCESS;
import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_UNKNOWN; import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_UNKNOWN;
import static android.service.voice.HotwordDetectionService.KEY_INITIALIZATION_STATUS; import static android.service.voice.HotwordDetectionService.KEY_INITIALIZATION_STATUS;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_ERROR;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_SUCCESS;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_UNKNOWN_NO_VALUE;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_UNKNOWN_OVER_MAX_CUSTOM_VALUE;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_UNKNOWN_TIMEOUT;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__AUDIO_SERVICE_DIED;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__SCHEDULE;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_UPDATE_STATE_AFTER_TIMEOUT;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__ON_CONNECTED;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE_FAIL;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_UPDATE_STATE;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECTED;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_EXCEPTION;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_TIMEOUT;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.content.ComponentName; import android.content.ComponentName;
@@ -89,12 +110,24 @@ final class HotwordDetectionConnection {
static final boolean DEBUG = false; static final boolean DEBUG = false;
// TODO: These constants need to be refined. // TODO: These constants need to be refined.
private static final long VALIDATION_TIMEOUT_MILLIS = 3000; private static final long VALIDATION_TIMEOUT_MILLIS = 4000;
private static final long MAX_UPDATE_TIMEOUT_MILLIS = 6000; private static final long MAX_UPDATE_TIMEOUT_MILLIS = 6000;
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
// Hotword metrics
private static final int METRICS_INIT_UNKNOWN_TIMEOUT =
HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_UNKNOWN_TIMEOUT;
private static final int METRICS_INIT_UNKNOWN_NO_VALUE =
HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_UNKNOWN_NO_VALUE;
private static final int METRICS_INIT_UNKNOWN_OVER_MAX_CUSTOM_VALUE =
HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_UNKNOWN_OVER_MAX_CUSTOM_VALUE;
private static final int METRICS_INIT_CALLBACK_STATE_ERROR =
HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_ERROR;
private static final int METRICS_INIT_CALLBACK_STATE_SUCCESS =
HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_SUCCESS;
private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool(); private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool();
// 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 =
@@ -103,6 +136,7 @@ final class HotwordDetectionConnection {
private final IBinder.DeathRecipient mAudioServerDeathRecipient = this::audioServerDied; private final IBinder.DeathRecipient mAudioServerDeathRecipient = this::audioServerDied;
private final @NonNull ServiceConnectionFactory mServiceConnectionFactory; private final @NonNull ServiceConnectionFactory mServiceConnectionFactory;
private final IHotwordRecognitionStatusCallback mCallback; private final IHotwordRecognitionStatusCallback mCallback;
private final int mDetectorType;
final Object mLock; final Object mLock;
final int mVoiceInteractionServiceUid; final int mVoiceInteractionServiceUid;
@@ -114,6 +148,7 @@ final class HotwordDetectionConnection {
private Instant mLastRestartInstant; private Instant mLastRestartInstant;
private ScheduledFuture<?> mCancellationTaskFuture; private ScheduledFuture<?> mCancellationTaskFuture;
private ScheduledFuture<?> mCancellationKeyPhraseDetectionFuture;
private ScheduledFuture<?> mDebugHotwordLoggingTimeoutFuture = null; private ScheduledFuture<?> mDebugHotwordLoggingTimeoutFuture = null;
/** Identity used for attributing app ops when delivering data to the Interactor. */ /** Identity used for attributing app ops when delivering data to the Interactor. */
@@ -134,7 +169,7 @@ final class HotwordDetectionConnection {
Identity voiceInteractorIdentity, ComponentName serviceName, int userId, Identity voiceInteractorIdentity, ComponentName serviceName, int userId,
boolean bindInstantServiceAllowed, @Nullable PersistableBundle options, boolean bindInstantServiceAllowed, @Nullable PersistableBundle options,
@Nullable SharedMemory sharedMemory, @Nullable SharedMemory sharedMemory,
@NonNull IHotwordRecognitionStatusCallback callback) { @NonNull IHotwordRecognitionStatusCallback callback, int detectorType) {
if (callback == null) { if (callback == null) {
Slog.w(TAG, "Callback is null while creating connection"); Slog.w(TAG, "Callback is null while creating connection");
throw new IllegalArgumentException("Callback is null while creating connection"); throw new IllegalArgumentException("Callback is null while creating connection");
@@ -146,6 +181,7 @@ final class HotwordDetectionConnection {
mDetectionComponentName = serviceName; mDetectionComponentName = serviceName;
mUser = userId; mUser = userId;
mCallback = callback; mCallback = callback;
mDetectorType = detectorType;
final Intent intent = new Intent(HotwordDetectionService.SERVICE_INTERFACE); final Intent intent = new Intent(HotwordDetectionService.SERVICE_INTERFACE);
intent.setComponent(mDetectionComponentName); intent.setComponent(mDetectionComponentName);
initAudioFlingerLocked(); initAudioFlingerLocked();
@@ -163,6 +199,8 @@ final class HotwordDetectionConnection {
Slog.v(TAG, "Time to restart the process, TTL has passed"); Slog.v(TAG, "Time to restart the process, TTL has passed");
synchronized (mLock) { synchronized (mLock) {
restartProcessLocked(); restartProcessLocked();
HotwordMetricsLogger.writeServiceRestartEvent(mDetectorType,
HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__SCHEDULE);
} }
}, 30, 30, TimeUnit.MINUTES); }, 30, 30, TimeUnit.MINUTES);
} }
@@ -195,6 +233,8 @@ final class HotwordDetectionConnection {
// We restart the process instead of simply sending over the new binder, to avoid race // We restart the process instead of simply sending over the new binder, to avoid race
// conditions with audio reading in the service. // conditions with audio reading in the service.
restartProcessLocked(); restartProcessLocked();
HotwordMetricsLogger.writeServiceRestartEvent(mDetectorType,
HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__AUDIO_SERVICE_DIED);
} }
} }
@@ -214,26 +254,30 @@ final class HotwordDetectionConnection {
future.complete(null); future.complete(null);
if (mUpdateStateAfterStartFinished.getAndSet(true)) { if (mUpdateStateAfterStartFinished.getAndSet(true)) {
Slog.w(TAG, "call callback after timeout"); Slog.w(TAG, "call callback after timeout");
HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_UPDATE_STATE_AFTER_TIMEOUT,
mVoiceInteractionServiceUid);
return; return;
} }
int status = bundle != null ? bundle.getInt( Pair<Integer, Integer> statusResultPair = getInitStatusAndMetricsResult(bundle);
KEY_INITIALIZATION_STATUS, int status = statusResultPair.first;
INITIALIZATION_STATUS_UNKNOWN) int initResultMetricsResult = statusResultPair.second;
: INITIALIZATION_STATUS_UNKNOWN;
// Add the protection to avoid unexpected status
if (status > HotwordDetectionService.getMaxCustomInitializationStatus()
&& status != INITIALIZATION_STATUS_UNKNOWN) {
status = INITIALIZATION_STATUS_UNKNOWN;
}
try { try {
mCallback.onStatusReported(status); mCallback.onStatusReported(status);
HotwordMetricsLogger.writeServiceInitResultEvent(mDetectorType,
initResultMetricsResult);
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.w(TAG, "Failed to report initialization status: " + e); Slog.w(TAG, "Failed to report initialization status: " + e);
HotwordMetricsLogger.writeServiceInitResultEvent(mDetectorType,
METRICS_INIT_CALLBACK_STATE_ERROR);
} }
} }
}; };
try { try {
service.updateState(options, sharedMemory, statusCallback); service.updateState(options, sharedMemory, statusCallback);
HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_UPDATE_STATE,
mVoiceInteractionServiceUid);
} catch (RemoteException e) { } catch (RemoteException e) {
// TODO: (b/181842909) Report an error to voice interactor // TODO: (b/181842909) Report an error to voice interactor
Slog.w(TAG, "Failed to updateState for HotwordDetectionService", e); Slog.w(TAG, "Failed to updateState for HotwordDetectionService", e);
@@ -248,8 +292,12 @@ final class HotwordDetectionConnection {
} }
try { try {
mCallback.onStatusReported(INITIALIZATION_STATUS_UNKNOWN); mCallback.onStatusReported(INITIALIZATION_STATUS_UNKNOWN);
HotwordMetricsLogger.writeServiceInitResultEvent(mDetectorType,
METRICS_INIT_UNKNOWN_TIMEOUT);
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.w(TAG, "Failed to report initialization status UNKNOWN", e); Slog.w(TAG, "Failed to report initialization status UNKNOWN", e);
HotwordMetricsLogger.writeServiceInitResultEvent(mDetectorType,
METRICS_INIT_CALLBACK_STATE_ERROR);
} }
} else if (err != null) { } else if (err != null) {
Slog.w(TAG, "Failed to update state: " + err); Slog.w(TAG, "Failed to update state: " + err);
@@ -259,6 +307,24 @@ final class HotwordDetectionConnection {
}); });
} }
private static Pair<Integer, Integer> getInitStatusAndMetricsResult(Bundle bundle) {
if (bundle == null) {
return new Pair<>(INITIALIZATION_STATUS_UNKNOWN, METRICS_INIT_UNKNOWN_NO_VALUE);
}
int status = bundle.getInt(KEY_INITIALIZATION_STATUS, INITIALIZATION_STATUS_UNKNOWN);
if (status > HotwordDetectionService.getMaxCustomInitializationStatus()) {
return new Pair<>(INITIALIZATION_STATUS_UNKNOWN,
status == INITIALIZATION_STATUS_UNKNOWN
? METRICS_INIT_UNKNOWN_NO_VALUE
:METRICS_INIT_UNKNOWN_OVER_MAX_CUSTOM_VALUE);
}
// TODO: should guard against negative here
int metricsResult = status == INITIALIZATION_STATUS_SUCCESS
? METRICS_INIT_CALLBACK_STATE_SUCCESS
: METRICS_INIT_CALLBACK_STATE_ERROR;
return new Pair<>(status, metricsResult);
}
private boolean isBound() { private boolean isBound() {
synchronized (mLock) { synchronized (mLock) {
return mRemoteHotwordDetectionService.isBound(); return mRemoteHotwordDetectionService.isBound();
@@ -473,12 +539,31 @@ final class HotwordDetectionConnection {
Slog.d(TAG, "onDetected"); Slog.d(TAG, "onDetected");
} }
synchronized (mLock) { synchronized (mLock) {
// TODO: If the dsp trigger comes in after the timeout, we will log both events.
// Because we don't enforce the timeout yet. We should add some synchronizations
// within the runnable to prevent the race condition to log both events.
if (mCancellationKeyPhraseDetectionFuture != null) {
mCancellationKeyPhraseDetectionFuture.cancel(true);
}
HotwordMetricsLogger.writeKeyphraseTriggerEvent(
mDetectorType,
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECTED);
if (!mValidatingDspTrigger) { if (!mValidatingDspTrigger) {
Slog.i(TAG, "Ignoring #onDetected due to a process restart"); Slog.i(TAG, "Ignoring #onDetected due to a process restart");
HotwordMetricsLogger.writeKeyphraseTriggerEvent(
mDetectorType,
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_EXCEPTION);
return; return;
} }
mValidatingDspTrigger = false; mValidatingDspTrigger = false;
enforcePermissionsForDataDelivery(); try {
enforcePermissionsForDataDelivery();
} catch (SecurityException e) {
HotwordMetricsLogger.writeKeyphraseTriggerEvent(
mDetectorType,
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_EXCEPTION);
throw e;
}
externalCallback.onKeyphraseDetected(recognitionEvent, result); externalCallback.onKeyphraseDetected(recognitionEvent, result);
if (result != null) { if (result != null) {
Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(result) Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(result)
@@ -496,8 +581,17 @@ final class HotwordDetectionConnection {
Slog.d(TAG, "onRejected"); Slog.d(TAG, "onRejected");
} }
synchronized (mLock) { synchronized (mLock) {
if (mCancellationKeyPhraseDetectionFuture != null) {
mCancellationKeyPhraseDetectionFuture.cancel(true);
}
HotwordMetricsLogger.writeKeyphraseTriggerEvent(
mDetectorType,
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED);
if (!mValidatingDspTrigger) { if (!mValidatingDspTrigger) {
Slog.i(TAG, "Ignoring #onRejected due to a process restart"); Slog.i(TAG, "Ignoring #onRejected due to a process restart");
HotwordMetricsLogger.writeKeyphraseTriggerEvent(
mDetectorType,
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_EXCEPTION);
return; return;
} }
mValidatingDspTrigger = false; mValidatingDspTrigger = false;
@@ -512,11 +606,20 @@ final class HotwordDetectionConnection {
synchronized (mLock) { synchronized (mLock) {
mValidatingDspTrigger = true; mValidatingDspTrigger = true;
mRemoteHotwordDetectionService.run( mRemoteHotwordDetectionService.run(
service -> service.detectFromDspSource( service -> {
recognitionEvent, // TODO: avoid allocate every time
recognitionEvent.getCaptureFormat(), mCancellationKeyPhraseDetectionFuture = mScheduledExecutorService.schedule(
VALIDATION_TIMEOUT_MILLIS, () -> HotwordMetricsLogger
internalCallback)); .writeKeyphraseTriggerEvent(mDetectorType,
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_TIMEOUT),
VALIDATION_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS);
service.detectFromDspSource(
recognitionEvent,
recognitionEvent.getCaptureFormat(),
VALIDATION_TIMEOUT_MILLIS,
internalCallback);
});
} }
} }
@@ -617,10 +720,16 @@ final class HotwordDetectionConnection {
} }
final boolean useHotwordDetectionService = mHotwordDetectionConnection != null; final boolean useHotwordDetectionService = mHotwordDetectionConnection != null;
if (useHotwordDetectionService) { if (useHotwordDetectionService) {
HotwordMetricsLogger.writeKeyphraseTriggerEvent(
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP,
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER);
mRecognitionEvent = recognitionEvent; mRecognitionEvent = recognitionEvent;
mHotwordDetectionConnection.detectFromDspSource( mHotwordDetectionConnection.detectFromDspSource(
recognitionEvent, mExternalCallback); recognitionEvent, mExternalCallback);
} else { } else {
HotwordMetricsLogger.writeKeyphraseTriggerEvent(
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR,
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER);
mExternalCallback.onKeyphraseDetected(recognitionEvent, null); mExternalCallback.onKeyphraseDetected(recognitionEvent, null);
} }
} }
@@ -783,6 +892,7 @@ final class HotwordDetectionConnection {
private boolean mRespectServiceConnectionStatusChanged = true; private boolean mRespectServiceConnectionStatusChanged = true;
private boolean mIsBound = false; private boolean mIsBound = false;
private boolean mIsLoggedFirstConnect = false;
ServiceConnection(@NonNull Context context, ServiceConnection(@NonNull Context context,
@NonNull Intent intent, int bindingFlags, int userId, @NonNull Intent intent, int bindingFlags, int userId,
@@ -806,6 +916,12 @@ final class HotwordDetectionConnection {
return; return;
} }
mIsBound = connected; mIsBound = connected;
if (connected && !mIsLoggedFirstConnect) {
mIsLoggedFirstConnect = true;
HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
HOTWORD_DETECTOR_EVENTS__EVENT__ON_CONNECTED,
mVoiceInteractionServiceUid);
}
} }
} }
@@ -836,13 +952,25 @@ final class HotwordDetectionConnection {
protected boolean bindService( protected boolean bindService(
@NonNull android.content.ServiceConnection serviceConnection) { @NonNull android.content.ServiceConnection serviceConnection) {
try { try {
return mContext.bindIsolatedService( HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE,
mVoiceInteractionServiceUid);
boolean bindResult = mContext.bindIsolatedService(
mIntent, mIntent,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE | mBindingFlags, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE | mBindingFlags,
"hotword_detector_" + mInstanceNumber, "hotword_detector_" + mInstanceNumber,
mExecutor, mExecutor,
serviceConnection); serviceConnection);
if (!bindResult) {
HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE_FAIL,
mVoiceInteractionServiceUid);
}
return bindResult;
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE_FAIL,
mVoiceInteractionServiceUid);
Slog.wtf(TAG, "Can't bind to the hotword detection service!", e); Slog.wtf(TAG, "Can't bind to the hotword detection service!", e);
return false; return false;
} }

View File

@@ -0,0 +1,154 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.voiceinteraction;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__DETECTOR_TYPE__NORMAL_DETECTOR;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_RESTARTED__DETECTOR_TYPE__NORMAL_DETECTOR;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_RESTARTED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_RESTARTED__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_CREATE_REQUESTED__DETECTOR_TYPE__NORMAL_DETECTOR;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_CREATE_REQUESTED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_CREATE_REQUESTED__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__DETECTOR_TYPE__NORMAL_DETECTOR;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
import android.service.voice.HotwordDetector;
import com.android.internal.util.FrameworkStatsLog;
/**
* A utility class for logging hotword statistics event.
*/
public final class HotwordMetricsLogger {
private static final int METRICS_INIT_DETECTOR_SOFTWARE =
HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
private static final int METRICS_INIT_DETECTOR_DSP =
HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
private static final int METRICS_INIT_NORMAL_DETECTOR =
HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__DETECTOR_TYPE__NORMAL_DETECTOR;
private HotwordMetricsLogger() {
// Class only contains static utility functions, and should not be instantiated
}
/**
* Logs information related to create hotword detector.
*/
public static void writeDetectorCreateEvent(int detectorType, boolean isCreated, int uid) {
int metricsDetectorType = getCreateMetricsDetectorType(detectorType);
FrameworkStatsLog.write(FrameworkStatsLog.HOTWORD_DETECTOR_CREATE_REQUESTED,
metricsDetectorType, isCreated, uid);
}
/**
* Logs information related to hotword detection service init result.
*/
public static void writeServiceInitResultEvent(int detectorType, int result) {
int metricsDetectorType = getInitMetricsDetectorType(detectorType);
FrameworkStatsLog.write(FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED,
metricsDetectorType, result);
}
/**
* Logs information related to hotword detection service restarting.
*/
public static void writeServiceRestartEvent(int detectorType, int reason) {
int metricsDetectorType = getRestartMetricsDetectorType(detectorType);
FrameworkStatsLog.write(FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_RESTARTED,
metricsDetectorType, reason);
}
/**
* Logs information related to keyphrase trigger.
*/
public static void writeKeyphraseTriggerEvent(int detectorType, int result) {
int metricsDetectorType = getKeyphraseMetricsDetectorType(detectorType);
FrameworkStatsLog.write(FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED,
metricsDetectorType, result);
}
/**
* Logs information related to hotword detector events.
*/
public static void writeDetectorEvent(int detectorType, int event, int uid) {
int metricsDetectorType = getDetectorMetricsDetectorType(detectorType);
FrameworkStatsLog.write(FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS,
metricsDetectorType, event, uid);
}
private static int getCreateMetricsDetectorType(int detectorType) {
switch (detectorType) {
case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE:
return HOTWORD_DETECTOR_CREATE_REQUESTED__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP:
return HOTWORD_DETECTOR_CREATE_REQUESTED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
default:
return HOTWORD_DETECTOR_CREATE_REQUESTED__DETECTOR_TYPE__NORMAL_DETECTOR;
}
}
private static int getRestartMetricsDetectorType(int detectorType) {
switch (detectorType) {
case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE:
return HOTWORD_DETECTION_SERVICE_RESTARTED__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP:
return HOTWORD_DETECTION_SERVICE_RESTARTED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
default:
return HOTWORD_DETECTION_SERVICE_RESTARTED__DETECTOR_TYPE__NORMAL_DETECTOR;
}
}
private static int getInitMetricsDetectorType(int detectorType) {
switch (detectorType) {
case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE:
return METRICS_INIT_DETECTOR_SOFTWARE;
case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP:
return METRICS_INIT_DETECTOR_DSP;
default:
return METRICS_INIT_NORMAL_DETECTOR;
}
}
private static int getKeyphraseMetricsDetectorType(int detectorType) {
switch (detectorType) {
case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE:
return HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP:
return HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
default:
return HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR;
}
}
private static int getDetectorMetricsDetectorType(int detectorType) {
switch (detectorType) {
case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE:
return HOTWORD_DETECTOR_EVENTS__DETECTOR_TYPE__TRUSTED_DETECTOR_SOFTWARE;
case HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP:
return HOTWORD_DETECTOR_EVENTS__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
default:
return HOTWORD_DETECTOR_EVENTS__DETECTOR_TYPE__NORMAL_DETECTOR;
}
}
}

View File

@@ -1168,7 +1168,8 @@ public class VoiceInteractionManagerService extends SystemService {
@NonNull Identity voiceInteractorIdentity, @NonNull Identity voiceInteractorIdentity,
@Nullable PersistableBundle options, @Nullable PersistableBundle options,
@Nullable SharedMemory sharedMemory, @Nullable SharedMemory sharedMemory,
IHotwordRecognitionStatusCallback callback) { IHotwordRecognitionStatusCallback callback,
int detectorType) {
enforceCallingPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION); enforceCallingPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION);
synchronized (this) { synchronized (this) {
enforceIsCurrentVoiceInteractionService(); enforceIsCurrentVoiceInteractionService();
@@ -1184,7 +1185,7 @@ public class VoiceInteractionManagerService extends SystemService {
final long caller = Binder.clearCallingIdentity(); final long caller = Binder.clearCallingIdentity();
try { try {
mImpl.updateStateLocked( mImpl.updateStateLocked(
voiceInteractorIdentity, options, sharedMemory, callback); voiceInteractorIdentity, options, sharedMemory, callback, detectorType);
} finally { } finally {
Binder.restoreCallingIdentity(caller); Binder.restoreCallingIdentity(caller);
} }

View File

@@ -53,6 +53,7 @@ import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.SharedMemory; import android.os.SharedMemory;
import android.os.UserHandle; import android.os.UserHandle;
import android.service.voice.HotwordDetector;
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback; import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.service.voice.IVoiceInteractionService; import android.service.voice.IVoiceInteractionService;
import android.service.voice.IVoiceInteractionSession; import android.service.voice.IVoiceInteractionSession;
@@ -102,6 +103,7 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
VoiceInteractionSessionConnection mActiveSession; VoiceInteractionSessionConnection mActiveSession;
int mDisabledShowContext; int mDisabledShowContext;
int mDetectorType;
final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override @Override
@@ -456,26 +458,36 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
@NonNull Identity voiceInteractorIdentity, @NonNull Identity voiceInteractorIdentity,
@Nullable PersistableBundle options, @Nullable PersistableBundle options,
@Nullable SharedMemory sharedMemory, @Nullable SharedMemory sharedMemory,
IHotwordRecognitionStatusCallback callback) { IHotwordRecognitionStatusCallback callback,
int detectorType) {
Slog.v(TAG, "updateStateLocked"); Slog.v(TAG, "updateStateLocked");
int voiceInteractionServiceUid = mInfo.getServiceInfo().applicationInfo.uid;
if (mHotwordDetectionComponentName == null) { if (mHotwordDetectionComponentName == null) {
Slog.w(TAG, "Hotword detection service name not found"); Slog.w(TAG, "Hotword detection service name not found");
logDetectorCreateEventIfNeeded(callback, detectorType, false,
voiceInteractionServiceUid);
throw new IllegalStateException("Hotword detection service name not found"); throw new IllegalStateException("Hotword detection service name not found");
} }
ServiceInfo hotwordDetectionServiceInfo = getServiceInfoLocked( ServiceInfo hotwordDetectionServiceInfo = getServiceInfoLocked(
mHotwordDetectionComponentName, mUser); mHotwordDetectionComponentName, mUser);
if (hotwordDetectionServiceInfo == null) { if (hotwordDetectionServiceInfo == null) {
Slog.w(TAG, "Hotword detection service info not found"); Slog.w(TAG, "Hotword detection service info not found");
logDetectorCreateEventIfNeeded(callback, detectorType, false,
voiceInteractionServiceUid);
throw new IllegalStateException("Hotword detection service info not found"); throw new IllegalStateException("Hotword detection service info not found");
} }
if (!isIsolatedProcessLocked(hotwordDetectionServiceInfo)) { if (!isIsolatedProcessLocked(hotwordDetectionServiceInfo)) {
Slog.w(TAG, "Hotword detection service not in isolated process"); Slog.w(TAG, "Hotword detection service not in isolated process");
logDetectorCreateEventIfNeeded(callback, detectorType, false,
voiceInteractionServiceUid);
throw new IllegalStateException("Hotword detection service not in isolated process"); throw new IllegalStateException("Hotword detection service not in isolated process");
} }
if (!Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE.equals( if (!Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE.equals(
hotwordDetectionServiceInfo.permission)) { hotwordDetectionServiceInfo.permission)) {
Slog.w(TAG, "Hotword detection service does not require permission " Slog.w(TAG, "Hotword detection service does not require permission "
+ Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE); + Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE);
logDetectorCreateEventIfNeeded(callback, detectorType, false,
voiceInteractionServiceUid);
throw new SecurityException("Hotword detection service does not require permission " throw new SecurityException("Hotword detection service does not require permission "
+ Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE); + Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE);
} }
@@ -484,25 +496,40 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
mInfo.getServiceInfo().packageName) == PackageManager.PERMISSION_GRANTED) { mInfo.getServiceInfo().packageName) == PackageManager.PERMISSION_GRANTED) {
Slog.w(TAG, "Voice interaction service should not hold permission " Slog.w(TAG, "Voice interaction service should not hold permission "
+ Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE); + Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE);
logDetectorCreateEventIfNeeded(callback, detectorType, false,
voiceInteractionServiceUid);
throw new SecurityException("Voice interaction service should not hold permission " throw new SecurityException("Voice interaction service should not hold permission "
+ Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE); + Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE);
} }
if (sharedMemory != null && !sharedMemory.setProtect(OsConstants.PROT_READ)) { if (sharedMemory != null && !sharedMemory.setProtect(OsConstants.PROT_READ)) {
Slog.w(TAG, "Can't set sharedMemory to be read-only"); Slog.w(TAG, "Can't set sharedMemory to be read-only");
logDetectorCreateEventIfNeeded(callback, detectorType, false,
voiceInteractionServiceUid);
throw new IllegalStateException("Can't set sharedMemory to be read-only"); throw new IllegalStateException("Can't set sharedMemory to be read-only");
} }
mDetectorType = detectorType;
logDetectorCreateEventIfNeeded(callback, detectorType, true,
voiceInteractionServiceUid);
if (mHotwordDetectionConnection == null) { if (mHotwordDetectionConnection == null) {
mHotwordDetectionConnection = new HotwordDetectionConnection(mServiceStub, mContext, mHotwordDetectionConnection = new HotwordDetectionConnection(mServiceStub, mContext,
mInfo.getServiceInfo().applicationInfo.uid, voiceInteractorIdentity, mInfo.getServiceInfo().applicationInfo.uid, voiceInteractorIdentity,
mHotwordDetectionComponentName, mUser, /* bindInstantServiceAllowed= */ false, mHotwordDetectionComponentName, mUser, /* bindInstantServiceAllowed= */ false,
options, sharedMemory, callback); options, sharedMemory, callback, detectorType);
} else { } else {
mHotwordDetectionConnection.updateStateLocked(options, sharedMemory); mHotwordDetectionConnection.updateStateLocked(options, sharedMemory);
} }
} }
private void logDetectorCreateEventIfNeeded(IHotwordRecognitionStatusCallback callback,
int detectorType, boolean isCreated, int voiceInteractionServiceUid) {
if (callback != null) {
HotwordMetricsLogger.writeDetectorCreateEvent(detectorType, true,
voiceInteractionServiceUid);
}
}
public void shutdownHotwordDetectionServiceLocked() { public void shutdownHotwordDetectionServiceLocked() {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "shutdownHotwordDetectionServiceLocked"); Slog.d(TAG, "shutdownHotwordDetectionServiceLocked");
@@ -667,6 +694,8 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
pw.println(Integer.toHexString(mDisabledShowContext)); pw.println(Integer.toHexString(mDisabledShowContext));
} }
pw.print(" mBound="); pw.print(mBound); pw.print(" mService="); pw.println(mService); pw.print(" mBound="); pw.print(mBound); pw.print(" mService="); pw.println(mService);
pw.print(" mDetectorType=");
pw.println(HotwordDetector.detectorTypeToString(mDetectorType));
if (mHotwordDetectionConnection != null) { if (mHotwordDetectionConnection != null) {
pw.println(" Hotword detection connection:"); pw.println(" Hotword detection connection:");
mHotwordDetectionConnection.dump(" ", pw); mHotwordDetectionConnection.dump(" ", pw);