Re-add partial hotword indicator exemption
The hotword indicator exemption was totally removed, instead of conditionally removed on UDC. Correct the implementation so devices without the hds_required sysprop set do not display the indicator when hds/trusted is not used. If HDS is not required, treat non-HDS VIService clients as trusted from the ST perspective. Then fire an RECORD_AUDIO_HOTWORD op when detection occurs (non-HDS clients only). This op is conditionally reported to the user if HDS is required. Addtl notes - Plumb args to do permission checks in the detector callback - Handle lack of perms identical to ST stack (return a pause) - Add logging for the requirement This does not change any HDS behavior. Fixes: 291869628 Test: CtsVoiceInteractionTestCases Test: Indicator behavior on 2nd stage fail w + w/o requirement set for non-HDS Change-Id: I7be92224bccf6f715ca492a2ffb124b242001cf1
This commit is contained in:
@@ -27,6 +27,7 @@ import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPH
|
||||
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__SERVICE_CRASH;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.compat.annotation.ChangeId;
|
||||
@@ -548,13 +549,15 @@ final class HotwordDetectionConnection {
|
||||
static final class SoundTriggerCallback extends IRecognitionStatusCallback.Stub {
|
||||
private final HotwordDetectionConnection mHotwordDetectionConnection;
|
||||
private final IHotwordRecognitionStatusCallback mExternalCallback;
|
||||
private final int mVoiceInteractionServiceUid;
|
||||
private final Identity mVoiceInteractorIdentity;
|
||||
private final Context mContext;
|
||||
|
||||
SoundTriggerCallback(IHotwordRecognitionStatusCallback callback,
|
||||
HotwordDetectionConnection connection, int uid) {
|
||||
SoundTriggerCallback(Context context, IHotwordRecognitionStatusCallback callback,
|
||||
HotwordDetectionConnection connection, Identity voiceInteractorIdentity) {
|
||||
mContext = context;
|
||||
mHotwordDetectionConnection = connection;
|
||||
mExternalCallback = callback;
|
||||
mVoiceInteractionServiceUid = uid;
|
||||
mVoiceInteractorIdentity = voiceInteractorIdentity;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -568,15 +571,30 @@ final class HotwordDetectionConnection {
|
||||
HotwordMetricsLogger.writeKeyphraseTriggerEvent(
|
||||
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP,
|
||||
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER,
|
||||
mVoiceInteractionServiceUid);
|
||||
mVoiceInteractorIdentity.uid);
|
||||
mHotwordDetectionConnection.detectFromDspSource(
|
||||
recognitionEvent, mExternalCallback);
|
||||
} else {
|
||||
HotwordMetricsLogger.writeKeyphraseTriggerEvent(
|
||||
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR,
|
||||
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER,
|
||||
mVoiceInteractionServiceUid);
|
||||
mExternalCallback.onKeyphraseDetected(recognitionEvent, null);
|
||||
// We have to attribute ops here, since we configure all st clients as trusted to
|
||||
// enable a partial exemption.
|
||||
// TODO (b/292012931) remove once trusted uniformly required.
|
||||
int result = mContext.getSystemService(AppOpsManager.class)
|
||||
.noteOpNoThrow(AppOpsManager.OP_RECORD_AUDIO_HOTWORD,
|
||||
mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
|
||||
mVoiceInteractorIdentity.attributionTag,
|
||||
"Non-HDS keyphrase recognition to VoiceInteractionService");
|
||||
|
||||
if (result != AppOpsManager.MODE_ALLOWED) {
|
||||
Slog.w(TAG, "onKeyphraseDetected suppressed, permission check returned: "
|
||||
+ result);
|
||||
mExternalCallback.onRecognitionPaused();
|
||||
} else {
|
||||
HotwordMetricsLogger.writeKeyphraseTriggerEvent(
|
||||
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR,
|
||||
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER,
|
||||
mVoiceInteractorIdentity.uid);
|
||||
mExternalCallback.onKeyphraseDetected(recognitionEvent, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ import com.android.server.SystemService;
|
||||
import com.android.server.UiThread;
|
||||
import com.android.server.pm.UserManagerInternal;
|
||||
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
|
||||
import com.android.server.policy.AppOpsPolicy;
|
||||
import com.android.server.utils.Slogf;
|
||||
import com.android.server.utils.TimingsTraceAndSlog;
|
||||
import com.android.server.wm.ActivityTaskManagerInternal;
|
||||
@@ -336,6 +337,9 @@ public class VoiceInteractionManagerService extends SystemService {
|
||||
/** The start value of showSessionId */
|
||||
private static final int SHOW_SESSION_START_ID = 0;
|
||||
|
||||
private final boolean IS_HDS_REQUIRED = AppOpsPolicy.isHotwordDetectionServiceRequired(
|
||||
mContext.getPackageManager());
|
||||
|
||||
@GuardedBy("this")
|
||||
private int mShowSessionId = SHOW_SESSION_START_ID;
|
||||
|
||||
@@ -393,8 +397,14 @@ public class VoiceInteractionManagerService extends SystemService {
|
||||
}
|
||||
try (SafeCloseable ignored = PermissionUtil.establishIdentityDirect(
|
||||
originatorIdentity)) {
|
||||
if (!IS_HDS_REQUIRED) {
|
||||
// For devices which still have hotword exemption, any client (not just HDS
|
||||
// clients) are trusted.
|
||||
// TODO (b/292012931) remove once trusted uniformly required.
|
||||
forHotwordDetectionService = true;
|
||||
}
|
||||
return new SoundTriggerSession(mSoundTriggerInternal.attach(client,
|
||||
moduleProperties, forHotwordDetectionService));
|
||||
moduleProperties, forHotwordDetectionService), originatorIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1674,10 +1684,13 @@ public class VoiceInteractionManagerService extends SystemService {
|
||||
final SoundTriggerInternal.Session mSession;
|
||||
private IHotwordRecognitionStatusCallback mSessionExternalCallback;
|
||||
private IRecognitionStatusCallback mSessionInternalCallback;
|
||||
private final Identity mVoiceInteractorIdentity;
|
||||
|
||||
SoundTriggerSession(
|
||||
SoundTriggerInternal.Session session) {
|
||||
SoundTriggerInternal.Session session,
|
||||
Identity voiceInteractorIdentity) {
|
||||
mSession = session;
|
||||
mVoiceInteractorIdentity = voiceInteractorIdentity;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1731,7 +1744,8 @@ public class VoiceInteractionManagerService extends SystemService {
|
||||
if (mSessionExternalCallback == null
|
||||
|| mSessionInternalCallback == null
|
||||
|| callback.asBinder() != mSessionExternalCallback.asBinder()) {
|
||||
mSessionInternalCallback = createSoundTriggerCallbackLocked(callback);
|
||||
mSessionInternalCallback = createSoundTriggerCallbackLocked(callback,
|
||||
mVoiceInteractorIdentity);
|
||||
mSessionExternalCallback = callback;
|
||||
}
|
||||
}
|
||||
@@ -1752,7 +1766,8 @@ public class VoiceInteractionManagerService extends SystemService {
|
||||
if (mSessionExternalCallback == null
|
||||
|| mSessionInternalCallback == null
|
||||
|| callback.asBinder() != mSessionExternalCallback.asBinder()) {
|
||||
soundTriggerCallback = createSoundTriggerCallbackLocked(callback);
|
||||
soundTriggerCallback = createSoundTriggerCallbackLocked(callback,
|
||||
mVoiceInteractorIdentity);
|
||||
Slog.w(TAG, "stopRecognition() called with a different callback than"
|
||||
+ "startRecognition()");
|
||||
} else {
|
||||
@@ -2090,6 +2105,7 @@ public class VoiceInteractionManagerService extends SystemService {
|
||||
pw.println(" mTemporarilyDisabled: " + mTemporarilyDisabled);
|
||||
pw.println(" mCurUser: " + mCurUser);
|
||||
pw.println(" mCurUserSupported: " + mCurUserSupported);
|
||||
pw.println(" mIsHdsRequired: " + IS_HDS_REQUIRED);
|
||||
dumpSupportedUsers(pw, " ");
|
||||
mDbHelper.dump(pw);
|
||||
if (mImpl == null) {
|
||||
@@ -2165,11 +2181,13 @@ public class VoiceInteractionManagerService extends SystemService {
|
||||
}
|
||||
|
||||
private IRecognitionStatusCallback createSoundTriggerCallbackLocked(
|
||||
IHotwordRecognitionStatusCallback callback) {
|
||||
IHotwordRecognitionStatusCallback callback,
|
||||
Identity voiceInteractorIdentity) {
|
||||
if (mImpl == null) {
|
||||
return null;
|
||||
}
|
||||
return mImpl.createSoundTriggerCallbackLocked(callback);
|
||||
return mImpl.createSoundTriggerCallbackLocked(mContext, callback,
|
||||
voiceInteractorIdentity);
|
||||
}
|
||||
|
||||
class RoleObserver implements OnRoleHoldersChangedListener {
|
||||
|
||||
@@ -877,12 +877,13 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
|
||||
}
|
||||
|
||||
public IRecognitionStatusCallback createSoundTriggerCallbackLocked(
|
||||
IHotwordRecognitionStatusCallback callback) {
|
||||
Context context, IHotwordRecognitionStatusCallback callback,
|
||||
Identity voiceInteractorIdentity) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "createSoundTriggerCallbackLocked");
|
||||
}
|
||||
return new HotwordDetectionConnection.SoundTriggerCallback(callback,
|
||||
mHotwordDetectionConnection, mInfo.getServiceInfo().applicationInfo.uid);
|
||||
return new HotwordDetectionConnection.SoundTriggerCallback(context, callback,
|
||||
mHotwordDetectionConnection, voiceInteractorIdentity);
|
||||
}
|
||||
|
||||
private static ServiceInfo getServiceInfoLocked(@NonNull ComponentName componentName,
|
||||
|
||||
Reference in New Issue
Block a user