Merge "Rename remote hotword detection service usages"

This commit is contained in:
Charles Chen
2023-01-04 18:28:23 +00:00
committed by Android (Google) Code Review
4 changed files with 80 additions and 72 deletions

View File

@@ -93,21 +93,27 @@ import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* A class that provides trusted hotword detector to communicate with the {@link
* HotwordDetectionService}.
* A class that provides sandboxed detector to communicate with the {@link
* HotwordDetectionService} and {@link VisualQueryDetectionService}.
*
* This class provides the methods to do initialization with the {@link HotwordDetectionService}
* and handle external source detection. It also provides the methods to check if we can egress
* the data from the {@link HotwordDetectionService}.
* Trusted hotword detectors such as {@link SoftwareHotwordDetector} and
* {@link AlwaysOnHotwordDetector} will leverage this class to communitcate with
* {@link HotwordDetectionService}; similarly, {@link VisualQueryDetector} will communicate with
* {@link VisualQueryDetectionService}.
*
* This class provides the methods to do initialization with the {@link HotwordDetectionService} and
* {@link VisualQueryDetectionService} handles external source detection for
* {@link HotwordDetectionService}. It also provides the methods to check if we can egress the data
* from the {@link HotwordDetectionService} and {@link VisualQueryDetectionService}.
*
* The subclass should override the {@link #informRestartProcessLocked()} to handle the trusted
* process restart.
*/
abstract class HotwordDetectorSession {
private static final String TAG = "HotwordDetectorSession";
abstract class DetectorSession {
private static final String TAG = "DetectorSession";
static final boolean DEBUG = false;
private static final String OP_MESSAGE =
private static final String HOTWORD_DETECTION_OP_MESSAGE =
"Providing hotword detection result to VoiceInteractionService";
// The error codes are used for onError callback
@@ -173,7 +179,7 @@ abstract class HotwordDetectorSession {
@GuardedBy("mLock")
ParcelFileDescriptor mCurrentAudioSink;
@GuardedBy("mLock")
@NonNull HotwordDetectionConnection.ServiceConnection mRemoteHotwordDetectionService;
@NonNull HotwordDetectionConnection.ServiceConnection mRemoteDetectionService;
boolean mDebugHotwordLogging = false;
@GuardedBy("mLock")
private double mProximityMeters = PROXIMITY_UNKNOWN;
@@ -185,13 +191,13 @@ abstract class HotwordDetectorSession {
boolean mPerformingExternalSourceHotwordDetection;
@NonNull final IBinder mToken;
HotwordDetectorSession(
@NonNull HotwordDetectionConnection.ServiceConnection remoteHotwordDetectionService,
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) {
mRemoteHotwordDetectionService = remoteHotwordDetectionService;
mRemoteDetectionService = remoteDetectionService;
mLock = lock;
mContext = context;
mToken = token;
@@ -219,7 +225,7 @@ abstract class HotwordDetectorSession {
if (DEBUG) {
Slog.d(TAG, "updateStateAfterProcessStartLocked");
}
AndroidFuture<Void> voidFuture = mRemoteHotwordDetectionService.postAsync(service -> {
AndroidFuture<Void> voidFuture = mRemoteDetectionService.postAsync(service -> {
AndroidFuture<Void> future = new AndroidFuture<>();
IRemoteCallback statusCallback = new IRemoteCallback.Stub() {
@Override
@@ -319,7 +325,7 @@ abstract class HotwordDetectorSession {
Slog.v(TAG, "call updateStateAfterProcessStartLocked");
updateStateAfterProcessStartLocked(options, sharedMemory);
} else {
mRemoteHotwordDetectionService.run(
mRemoteDetectionService.run(
service -> service.updateState(options, sharedMemory, /* callback= */ null));
}
}
@@ -407,7 +413,7 @@ abstract class HotwordDetectorSession {
// TODO: handle cancellations well
// TODO: what if we cancelled and started a new one?
mRemoteHotwordDetectionService.run(
mRemoteDetectionService.run(
service -> {
service.detectFromMicrophoneSource(
serviceAudioSource,
@@ -512,7 +518,7 @@ abstract class HotwordDetectorSession {
void destroyLocked() {
mDestroyed = true;
mDebugHotwordLogging = false;
mRemoteHotwordDetectionService = null;
mRemoteDetectionService = null;
if (mAttentionManagerInternal != null) {
mAttentionManagerInternal.onStopProximityUpdates(mProximityCallbackInternal);
}
@@ -524,9 +530,9 @@ abstract class HotwordDetectorSession {
}
@SuppressWarnings("GuardedBy")
void updateRemoteHotwordDetectionServiceLocked(
@NonNull HotwordDetectionConnection.ServiceConnection remoteHotwordDetectionService) {
mRemoteHotwordDetectionService = remoteHotwordDetectionService;
void updateRemoteSandboxedDetectionServiceLocked(
@NonNull HotwordDetectionConnection.ServiceConnection remoteDetectionService) {
mRemoteDetectionService = remoteDetectionService;
}
void reportErrorLocked(int status) {
@@ -628,9 +634,9 @@ abstract class HotwordDetectorSession {
int hotwordOp = AppOpsManager.strOpToOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD);
mAppOpsManager.noteOpNoThrow(hotwordOp,
mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
mVoiceInteractorIdentity.attributionTag, OP_MESSAGE);
mVoiceInteractorIdentity.attributionTag, HOTWORD_DETECTION_OP_MESSAGE);
enforcePermissionForDataDelivery(mContext, mVoiceInteractorIdentity,
CAPTURE_AUDIO_HOTWORD, OP_MESSAGE);
CAPTURE_AUDIO_HOTWORD, HOTWORD_DETECTION_OP_MESSAGE);
}
});
}

View File

@@ -32,7 +32,6 @@ import android.os.IBinder;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SharedMemory;
import android.service.voice.AlwaysOnHotwordDetector;
import android.service.voice.HotwordDetectedResult;
import android.service.voice.HotwordDetectionService;
import android.service.voice.HotwordDetector;
@@ -59,7 +58,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* {@link android.service.voice.VoiceInteractionService#createAlwaysOnHotwordDetector(String,
* Locale, PersistableBundle, SharedMemory, AlwaysOnHotwordDetector.Callback)}.
*/
final class DspTrustedHotwordDetectorSession extends HotwordDetectorSession {
final class DspTrustedHotwordDetectorSession extends DetectorSession {
private static final String TAG = "DspTrustedHotwordDetectorSession";
// The validation timeout value is 3 seconds for onDetect of DSP trigger event.
@@ -182,7 +181,7 @@ final class DspTrustedHotwordDetectorSession extends HotwordDetectorSession {
};
mValidatingDspTrigger = true;
mRemoteHotwordDetectionService.run(service -> {
mRemoteDetectionService.run(service -> {
// We use the VALIDATION_TIMEOUT_MILLIS to inform that the client needs to invoke
// the callback before timeout value. In order to reduce the latency impact between
// server side and client side, we need to use another timeout value

View File

@@ -89,7 +89,7 @@ final class HotwordDetectionConnection {
Executors.newSingleThreadScheduledExecutor();
@Nullable private final ScheduledFuture<?> mCancellationTaskFuture;
private final IBinder.DeathRecipient mAudioServerDeathRecipient = this::audioServerDied;
@NonNull private final ServiceConnectionFactory mServiceConnectionFactory;
@NonNull private final ServiceConnectionFactory mHotwordDetectionServiceConnectionFactory;
private final int mDetectorType;
/**
* Time after which each HotwordDetectionService process is stopped and replaced by a new one.
@@ -99,7 +99,7 @@ final class HotwordDetectionConnection {
final Object mLock;
final int mVoiceInteractionServiceUid;
final ComponentName mDetectionComponentName;
final ComponentName mHotwordDetectionComponentName;
final int mUser;
final Context mContext;
volatile HotwordDetectionServiceIdentity mIdentity;
@@ -122,27 +122,30 @@ final class HotwordDetectionConnection {
* to record the detectors.
*/
@GuardedBy("mLock")
private final SparseArray<HotwordDetectorSession> mHotwordDetectorSessions =
private final SparseArray<DetectorSession> mDetectorSessions =
new SparseArray<>();
HotwordDetectionConnection(Object lock, Context context, int voiceInteractionServiceUid,
Identity voiceInteractorIdentity, ComponentName serviceName, int userId,
Identity voiceInteractorIdentity, ComponentName hotwordDetectionServiceName, int userId,
boolean bindInstantServiceAllowed, int detectorType) {
mLock = lock;
mContext = context;
mVoiceInteractionServiceUid = voiceInteractionServiceUid;
mVoiceInteractorIdentity = voiceInteractorIdentity;
mDetectionComponentName = serviceName;
mHotwordDetectionComponentName = hotwordDetectionServiceName;
mUser = userId;
mDetectorType = detectorType;
mReStartPeriodSeconds = DeviceConfig.getInt(DeviceConfig.NAMESPACE_VOICE_INTERACTION,
KEY_RESTART_PERIOD_IN_SECONDS, 0);
final Intent intent = new Intent(HotwordDetectionService.SERVICE_INTERFACE);
intent.setComponent(mDetectionComponentName);
final Intent hotwordDetectionServiceIntent =
new Intent(HotwordDetectionService.SERVICE_INTERFACE);
hotwordDetectionServiceIntent.setComponent(mHotwordDetectionComponentName);
initAudioFlingerLocked();
mServiceConnectionFactory = new ServiceConnectionFactory(intent, bindInstantServiceAllowed);
mRemoteHotwordDetectionService = mServiceConnectionFactory.createLocked();
mHotwordDetectionServiceConnectionFactory =
new ServiceConnectionFactory(hotwordDetectionServiceIntent,
bindInstantServiceAllowed);
mRemoteHotwordDetectionService = mHotwordDetectionServiceConnectionFactory.createLocked();
mLastRestartInstant = Instant.now();
if (mReStartPeriodSeconds <= 0) {
@@ -176,8 +179,8 @@ final class HotwordDetectionConnection {
try {
mAudioFlinger.linkToDeath(mAudioServerDeathRecipient, /* flags= */ 0);
} catch (RemoteException e) {
Slog.w(TAG, "Audio server died before we registered a DeathRecipient; retrying init.",
e);
Slog.w(TAG, "Audio server died before we registered a DeathRecipient; "
+ "retrying init.", e);
initAudioFlingerLocked();
}
}
@@ -200,10 +203,10 @@ final class HotwordDetectionConnection {
void cancelLocked() {
Slog.v(TAG, "cancelLocked");
clearDebugHotwordLoggingTimeoutLocked();
runForEachHotwordDetectorSessionLocked((session) -> {
runForEachDetectorSessionLocked((session) -> {
session.destroyLocked();
});
mHotwordDetectorSessions.clear();
mDetectorSessions.clear();
mDebugHotwordLogging = false;
mRemoteHotwordDetectionService.unbind();
LocalServices.getService(PermissionManagerServiceInternal.class)
@@ -223,7 +226,7 @@ final class HotwordDetectionConnection {
@SuppressWarnings("GuardedBy")
void updateStateLocked(PersistableBundle options, SharedMemory sharedMemory,
@NonNull IBinder token) {
final HotwordDetectorSession session = getDetectorSessionByTokenLocked(token);
final DetectorSession session = getDetectorSessionByTokenLocked(token);
if (session == null) {
Slog.v(TAG, "Not found the detector by token");
return;
@@ -259,7 +262,7 @@ final class HotwordDetectionConnection {
if (DEBUG) {
Slog.d(TAG, "startListeningFromExternalSourceLocked");
}
final HotwordDetectorSession session = getDetectorSessionByTokenLocked(token);
final DetectorSession session = getDetectorSessionByTokenLocked(token);
if (session == null) {
Slog.v(TAG, "Not found the detector by token");
return;
@@ -321,7 +324,7 @@ final class HotwordDetectionConnection {
Slog.v(TAG, "setDebugHotwordLoggingLocked: " + logging);
clearDebugHotwordLoggingTimeoutLocked();
mDebugHotwordLogging = logging;
runForEachHotwordDetectorSessionLocked((session) -> {
runForEachDetectorSessionLocked((session) -> {
session.setDebugHotwordLoggingLocked(logging);
});
@@ -331,7 +334,7 @@ final class HotwordDetectionConnection {
Slog.v(TAG, "Timeout to reset mDebugHotwordLogging to false");
synchronized (mLock) {
mDebugHotwordLogging = false;
runForEachHotwordDetectorSessionLocked((session) -> {
runForEachDetectorSessionLocked((session) -> {
session.setDebugHotwordLoggingLocked(false);
});
}
@@ -350,24 +353,24 @@ final class HotwordDetectionConnection {
private void restartProcessLocked() {
// TODO(b/244598068): Check HotwordAudioStreamManager first
Slog.v(TAG, "Restarting hotword detection process");
ServiceConnection oldConnection = mRemoteHotwordDetectionService;
ServiceConnection oldHotwordConnection = mRemoteHotwordDetectionService;
HotwordDetectionServiceIdentity previousIdentity = mIdentity;
mLastRestartInstant = Instant.now();
// Recreate connection to reset the cache.
mRemoteHotwordDetectionService = mServiceConnectionFactory.createLocked();
mRemoteHotwordDetectionService = mHotwordDetectionServiceConnectionFactory.createLocked();
Slog.v(TAG, "Started the new process, dispatching processRestarted to detector");
runForEachHotwordDetectorSessionLocked((session) -> {
session.updateRemoteHotwordDetectionServiceLocked(mRemoteHotwordDetectionService);
runForEachDetectorSessionLocked((session) -> {
session.updateRemoteSandboxedDetectionServiceLocked(mRemoteHotwordDetectionService);
session.informRestartProcessLocked();
});
if (DEBUG) {
Slog.i(TAG, "processRestarted is dispatched done, unbinding from the old process");
}
oldConnection.ignoreConnectionStatusEvents();
oldConnection.unbind();
oldHotwordConnection.ignoreConnectionStatusEvents();
oldHotwordConnection.unbind();
if (previousIdentity != null) {
removeServiceUidForAudioPolicy(previousIdentity.getIsolatedUid());
}
@@ -437,12 +440,12 @@ final class HotwordDetectionConnection {
pw.print(prefix); pw.print("mBound=");
pw.println(mRemoteHotwordDetectionService.isBound());
pw.print(prefix); pw.print("mRestartCount=");
pw.println(mServiceConnectionFactory.mRestartCount);
pw.println(mHotwordDetectionServiceConnectionFactory.mRestartCount);
pw.print(prefix); pw.print("mLastRestartInstant="); pw.println(mLastRestartInstant);
pw.print(prefix); pw.print("mDetectorType=");
pw.println(HotwordDetector.detectorTypeToString(mDetectorType));
pw.print(prefix); pw.println("HotwordDetectorSession(s)");
runForEachHotwordDetectorSessionLocked((session) -> {
pw.print(prefix); pw.println("DetectorSession(s)");
runForEachDetectorSessionLocked((session) -> {
session.dumpLocked(prefix, pw);
});
}
@@ -537,9 +540,9 @@ final class HotwordDetectionConnection {
}
}
synchronized (HotwordDetectionConnection.this.mLock) {
runForEachHotwordDetectorSessionLocked((session) -> {
runForEachDetectorSessionLocked((session) -> {
session.reportErrorLocked(
HotwordDetectorSession.HOTWORD_DETECTION_SERVICE_DIED);
DetectorSession.HOTWORD_DETECTION_SERVICE_DIED);
});
}
// Can improve to log exit reason if needed
@@ -599,12 +602,12 @@ final class HotwordDetectionConnection {
int detectorType) {
// We only support one Dsp trusted hotword detector and one software hotword detector at
// the same time, remove existing one.
HotwordDetectorSession removeSession = mHotwordDetectorSessions.get(detectorType);
DetectorSession removeSession = mDetectorSessions.get(detectorType);
if (removeSession != null) {
removeSession.destroyLocked();
mHotwordDetectorSessions.remove(detectorType);
mDetectorSessions.remove(detectorType);
}
final HotwordDetectorSession session;
final DetectorSession session;
if (detectorType == HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP) {
session = new DspTrustedHotwordDetectorSession(mRemoteHotwordDetectionService,
mLock, mContext, token, callback, mVoiceInteractionServiceUid,
@@ -615,30 +618,30 @@ final class HotwordDetectionConnection {
mVoiceInteractionServiceUid, mVoiceInteractorIdentity,
mScheduledExecutorService, mDebugHotwordLogging);
}
mHotwordDetectorSessions.put(detectorType, session);
mDetectorSessions.put(detectorType, session);
session.initialize(options, sharedMemory);
}
@SuppressWarnings("GuardedBy")
void destroyDetectorLocked(@NonNull IBinder token) {
final HotwordDetectorSession session = getDetectorSessionByTokenLocked(token);
final DetectorSession session = getDetectorSessionByTokenLocked(token);
if (session != null) {
session.destroyLocked();
final int index = mHotwordDetectorSessions.indexOfValue(session);
if (index < 0 || index > mHotwordDetectorSessions.size() - 1) {
final int index = mDetectorSessions.indexOfValue(session);
if (index < 0 || index > mDetectorSessions.size() - 1) {
return;
}
mHotwordDetectorSessions.removeAt(index);
mDetectorSessions.removeAt(index);
}
}
@SuppressWarnings("GuardedBy")
private HotwordDetectorSession getDetectorSessionByTokenLocked(IBinder token) {
private DetectorSession getDetectorSessionByTokenLocked(IBinder token) {
if (token == null) {
return null;
}
for (int i = 0; i < mHotwordDetectorSessions.size(); i++) {
final HotwordDetectorSession session = mHotwordDetectorSessions.valueAt(i);
for (int i = 0; i < mDetectorSessions.size(); i++) {
final DetectorSession session = mDetectorSessions.valueAt(i);
if (!session.isDestroyed() && session.isSameToken(token)) {
return session;
}
@@ -648,7 +651,7 @@ final class HotwordDetectionConnection {
@SuppressWarnings("GuardedBy")
private DspTrustedHotwordDetectorSession getDspTrustedHotwordDetectorSessionLocked() {
final HotwordDetectorSession session = mHotwordDetectorSessions.get(
final DetectorSession session = mDetectorSessions.get(
HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_DSP);
if (session == null || session.isDestroyed()) {
Slog.v(TAG, "Not found the Dsp detector");
@@ -659,7 +662,7 @@ final class HotwordDetectionConnection {
@SuppressWarnings("GuardedBy")
private SoftwareTrustedHotwordDetectorSession getSoftwareTrustedHotwordDetectorSessionLocked() {
final HotwordDetectorSession session = mHotwordDetectorSessions.get(
final DetectorSession session = mDetectorSessions.get(
HotwordDetector.DETECTOR_TYPE_TRUSTED_HOTWORD_SOFTWARE);
if (session == null || session.isDestroyed()) {
Slog.v(TAG, "Not found the software detector");
@@ -669,10 +672,10 @@ final class HotwordDetectionConnection {
}
@SuppressWarnings("GuardedBy")
private void runForEachHotwordDetectorSessionLocked(
@NonNull Consumer<HotwordDetectorSession> action) {
for (int i = 0; i < mHotwordDetectorSessions.size(); i++) {
HotwordDetectorSession session = mHotwordDetectorSessions.valueAt(i);
private void runForEachDetectorSessionLocked(
@NonNull Consumer<DetectorSession> action) {
for (int i = 0; i < mDetectorSessions.size(); i++) {
DetectorSession session = mDetectorSessions.valueAt(i);
action.accept(session);
}
}

View File

@@ -55,7 +55,7 @@ import java.util.concurrent.ScheduledExecutorService;
* {@link android.service.voice.VoiceInteractionService#createHotwordDetector(PersistableBundle,
* SharedMemory, HotwordDetector.Callback)}.
*/
final class SoftwareTrustedHotwordDetectorSession extends HotwordDetectorSession {
final class SoftwareTrustedHotwordDetectorSession extends DetectorSession {
private static final String TAG = "SoftwareTrustedHotwordDetectorSession";
private IMicrophoneHotwordDetectionVoiceInteractionCallback mSoftwareCallback;
@@ -155,7 +155,7 @@ final class SoftwareTrustedHotwordDetectorSession extends HotwordDetectorSession
}
};
mRemoteHotwordDetectionService.run(
mRemoteDetectionService.run(
service -> service.detectFromMicrophoneSource(
null,
AUDIO_SOURCE_MICROPHONE,
@@ -179,7 +179,7 @@ final class SoftwareTrustedHotwordDetectorSession extends HotwordDetectorSession
}
mPerformingSoftwareHotwordDetection = false;
mRemoteHotwordDetectionService.run(ISandboxedDetectionService::stopDetection);
mRemoteDetectionService.run(ISandboxedDetectionService::stopDetection);
closeExternalAudioStreamLocked("stopping requested");
}