diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java index e157a277366f5..a8ed70b91442c 100644 --- a/services/core/java/com/android/server/policy/AppOpsPolicy.java +++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java @@ -202,9 +202,8 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat } private static boolean isHotwordDetectionServiceRequired(PackageManager pm) { - // The HotwordDetectionService APIs aren't ready yet for Auto or TV. - return !(pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) - || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)); + // Usage of the HotwordDetectionService won't be enforced until a later release. + return false; } @Override diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java index f3d151fe5cc4f..24eb0ebe33117 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.RECORD_AUDIO; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.AppOpsManager; import android.content.Context; import android.content.PermissionChecker; import android.media.permission.Identity; @@ -112,16 +113,27 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware * originator temporarily doesn't have the right permissions to use this service. */ private void enforcePermissionsForPreflight(@NonNull Identity identity) { - enforcePermissionForPreflight(mContext, identity, RECORD_AUDIO); - enforcePermissionForPreflight(mContext, identity, CAPTURE_AUDIO_HOTWORD); + enforcePermissionForPreflight(mContext, identity, RECORD_AUDIO, + /* allowSoftDenial= */ true); + enforcePermissionForPreflight(mContext, identity, CAPTURE_AUDIO_HOTWORD, + /* allowSoftDenial= */ true); } /** * Throws a {@link SecurityException} iff the originator has permission to receive data. */ void enforcePermissionsForDataDelivery(@NonNull Identity identity, @NonNull String reason) { - enforcePermissionForDataDelivery(mContext, identity, RECORD_AUDIO, - reason); + // SoundTrigger data is treated the same as Hotword-source audio. This should incur the + // HOTWORD op instead of the RECORD_AUDIO op. The RECORD_AUDIO permission is still required, + // and since this is a data delivery check, soft denials aren't accepted. + // TODO(b/212458940): Find a better approach for checking the permission that doesn't + // require the client to know such details about the permissions logic. + enforcePermissionForPreflight(mContext, identity, RECORD_AUDIO, + /* allowSoftDenial= */ false); + int hotwordOp = AppOpsManager.strOpToOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD); + mContext.getSystemService(AppOpsManager.class).noteOpNoThrow(hotwordOp, identity.uid, + identity.packageName, identity.attributionTag, reason); + enforcePermissionForDataDelivery(mContext, identity, CAPTURE_AUDIO_HOTWORD, reason); } @@ -150,20 +162,25 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware /** * Throws a {@link SecurityException} if originator permanently doesn't have the given * permission. - * Soft (temporary) denials are considered OK for preflight purposes. * - * @param context A {@link Context}, used for permission checks. - * @param identity The identity to check. - * @param permission The identifier of the permission we want to check. + * @param context A {@link Context}, used for permission checks. + * @param identity The identity to check. + * @param permission The identifier of the permission we want to check. + * @param allowSoftDenial If true, the operation succeeds even for soft (temporary) denials. */ + // TODO: Consider splitting up this method instead of using `allowSoftDenial`, to make it + // clearer when soft denials are not allowed. private static void enforcePermissionForPreflight(@NonNull Context context, - @NonNull Identity identity, @NonNull String permission) { + @NonNull Identity identity, @NonNull String permission, boolean allowSoftDenial) { final int status = PermissionUtil.checkPermissionForPreflight(context, identity, permission); switch (status) { case PermissionChecker.PERMISSION_GRANTED: - case PermissionChecker.PERMISSION_SOFT_DENIED: return; + case PermissionChecker.PERMISSION_SOFT_DENIED: + if (allowSoftDenial) { + return; + } // else fall through case PermissionChecker.PERMISSION_HARD_DENIED: throw new SecurityException( String.format("Failed to obtain permission %s for identity %s", permission, diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 2cb3982047435..3610d8362d6a4 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -46,11 +46,9 @@ import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPH import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED; import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED_FROM_RESTART; import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECT_UNEXPECTED_CALLBACK; -import static com.android.server.voiceinteraction.SoundTriggerSessionPermissionsDecorator.enforcePermissionForPreflight; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.AppOpsManager; import android.content.ComponentName; import android.content.ContentCaptureOptions; import android.content.Context; @@ -1111,11 +1109,12 @@ final class HotwordDetectionConnection { // TODO: Share this code with SoundTriggerMiddlewarePermission. private void enforcePermissionsForDataDelivery() { Binder.withCleanCallingIdentity(() -> { - enforcePermissionForPreflight(mContext, mVoiceInteractorIdentity, RECORD_AUDIO); - int hotwordOp = AppOpsManager.strOpToOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD); - mContext.getSystemService(AppOpsManager.class).noteOpNoThrow(hotwordOp, - mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName, - mVoiceInteractorIdentity.attributionTag, OP_MESSAGE); + // Hack to make sure we show the mic privacy-indicator since the Trusted Hotword + // requirement isn't being enforced for now. Normally, we would note the HOTWORD op here + // instead. + enforcePermissionForDataDelivery(mContext, mVoiceInteractorIdentity, + RECORD_AUDIO, OP_MESSAGE); + enforcePermissionForDataDelivery(mContext, mVoiceInteractorIdentity, CAPTURE_AUDIO_HOTWORD, OP_MESSAGE); });