diff options
11 files changed, 101 insertions, 14 deletions
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 3b1943bf86f6..a216021fc66b 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1335,9 +1335,17 @@ public class AppOpsManager { public static final int OP_ACCESS_RESTRICTED_SETTINGS = AppProtoEnums.APP_OP_ACCESS_RESTRICTED_SETTINGS; + /** + * Receive microphone audio from an ambient sound detection event + * + * @hide + */ + public static final int OP_RECEIVE_AMBIENT_TRIGGER_AUDIO = + AppProtoEnums.APP_OP_RECEIVE_AMBIENT_TRIGGER_AUDIO; + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int _NUM_OP = 120; + public static final int _NUM_OP = 121; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -1800,6 +1808,14 @@ public class AppOpsManager { public static final String OPSTR_ACCESS_RESTRICTED_SETTINGS = "android:access_restricted_settings"; + /** + * Receive microphone audio from an ambient sound detection event + * + * @hide + */ + public static final String OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO = + "android:receive_ambient_trigger_audio"; + /** {@link #sAppOpsToNote} not initialized yet for this op */ private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0; /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */ @@ -2021,6 +2037,7 @@ public class AppOpsManager { OP_ESTABLISH_VPN_SERVICE, // OP_ESTABLISH_VPN_SERVICE OP_ESTABLISH_VPN_MANAGER, // OP_ESTABLISH_VPN_MANAGER OP_ACCESS_RESTRICTED_SETTINGS, // OP_ACCESS_RESTRICTED_SETTINGS + OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, // RECEIVE_SOUNDTRIGGER_AUDIO }; /** @@ -2147,6 +2164,7 @@ public class AppOpsManager { OPSTR_ESTABLISH_VPN_SERVICE, OPSTR_ESTABLISH_VPN_MANAGER, OPSTR_ACCESS_RESTRICTED_SETTINGS, + OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO, }; /** @@ -2274,6 +2292,7 @@ public class AppOpsManager { "ESTABLISH_VPN_SERVICE", "ESTABLISH_VPN_MANAGER", "ACCESS_RESTRICTED_SETTINGS", + "RECEIVE_SOUNDTRIGGER_AUDIO", }; /** @@ -2402,6 +2421,7 @@ public class AppOpsManager { null, // no permission for OP_ESTABLISH_VPN_SERVICE null, // no permission for OP_ESTABLISH_VPN_MANAGER null, // no permission for OP_ACCESS_RESTRICTED_SETTINGS, + null, // no permission for OP_RECEIVE_SOUNDTRIGGER_AUDIO }; /** @@ -2529,7 +2549,8 @@ public class AppOpsManager { null, // NEARBY_WIFI_DEVICES null, // ESTABLISH_VPN_SERVICE null, // ESTABLISH_VPN_MANAGER - null, // ACCESS_RESTRICTED_SETTINGS, + null, // ACCESS_RESTRICTED_SETTINGS + null, // RECEIVE_SOUNDTRIGGER_AUDIO }; /** @@ -2656,7 +2677,8 @@ public class AppOpsManager { null, // NEARBY_WIFI_DEVICES null, // ESTABLISH_VPN_SERVICE null, // ESTABLISH_VPN_MANAGER - null, // ACCESS_RESTRICTED_SETTINGS, + null, // ACCESS_RESTRICTED_SETTINGS + null, // RECEIVE_SOUNDTRIGGER_AUDIO }; /** @@ -2765,7 +2787,7 @@ public class AppOpsManager { AppOpsManager.MODE_ERRORED, // OP_NO_ISOLATED_STORAGE AppOpsManager.MODE_ALLOWED, // PHONE_CALL_MICROPHONE AppOpsManager.MODE_ALLOWED, // PHONE_CALL_CAMERA - AppOpsManager.MODE_ALLOWED, // OP_RECORD_AUDIO_HOTWORD + AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO_HOTWORD AppOpsManager.MODE_DEFAULT, // MANAGE_ONGOING_CALLS AppOpsManager.MODE_DEFAULT, // MANAGE_CREDENTIALS AppOpsManager.MODE_DEFAULT, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER @@ -2783,6 +2805,7 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, // ESTABLISH_VPN_SERVICE AppOpsManager.MODE_ALLOWED, // ESTABLISH_VPN_MANAGER AppOpsManager.MODE_ALLOWED, // ACCESS_RESTRICTED_SETTINGS, + AppOpsManager.MODE_ALLOWED, // RECEIVE_SOUNDTRIGGER_AUDIO }; /** @@ -2913,6 +2936,7 @@ public class AppOpsManager { false, // OP_ESTABLISH_VPN_SERVICE false, // OP_ESTABLISH_VPN_MANAGER true, // ACCESS_RESTRICTED_SETTINGS + false, // RECEIVE_SOUNDTRIGGER_AUDIO }; /** @@ -3040,6 +3064,7 @@ public class AppOpsManager { false, // OP_ESTABLISH_VPN_SERVICE false, // OP_ESTABLISH_VPN_MANAGER true, // ACCESS_RESTRICTED_SETTINGS + false, // RECEIVE_SOUNDTRIGGER_AUDIO }; /** diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java index 4ed939c48bd7..f5f1c374b636 100644 --- a/core/java/android/permission/PermissionUsageHelper.java +++ b/core/java/android/permission/PermissionUsageHelper.java @@ -30,6 +30,7 @@ import static android.app.AppOpsManager.OPSTR_COARSE_LOCATION; import static android.app.AppOpsManager.OPSTR_FINE_LOCATION; import static android.app.AppOpsManager.OPSTR_PHONE_CALL_CAMERA; import static android.app.AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE; +import static android.app.AppOpsManager.OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO; import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO; import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED; @@ -137,6 +138,7 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis private static final List<String> MIC_OPS = List.of( OPSTR_PHONE_CALL_MICROPHONE, + OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO, OPSTR_RECORD_AUDIO ); @@ -147,6 +149,7 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis private static @NonNull String getGroupForOp(String op) { switch (op) { + case OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO: case OPSTR_RECORD_AUDIO: return MICROPHONE; case OPSTR_CAMERA: diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index c10fafeac8b4..6b859763eb6f 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -102,6 +102,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon AppOpsManager.OP_PHONE_CALL_CAMERA, AppOpsManager.OP_SYSTEM_ALERT_WINDOW, AppOpsManager.OP_RECORD_AUDIO, + AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, AppOpsManager.OP_PHONE_CALL_MICROPHONE, AppOpsManager.OP_COARSE_LOCATION, AppOpsManager.OP_FINE_LOCATION @@ -535,7 +536,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon } private boolean isOpMicrophone(int op) { - return op == AppOpsManager.OP_RECORD_AUDIO || op == AppOpsManager.OP_PHONE_CALL_MICROPHONE; + return op == AppOpsManager.OP_RECORD_AUDIO || op == AppOpsManager.OP_PHONE_CALL_MICROPHONE + || op == AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO; } protected class H extends Handler { diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index dbdb9d268aab..cd6eb99e259e 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -58,7 +58,8 @@ class PrivacyItemController @Inject constructor( internal companion object { val OPS_MIC_CAMERA = intArrayOf(AppOpsManager.OP_CAMERA, AppOpsManager.OP_PHONE_CALL_CAMERA, AppOpsManager.OP_RECORD_AUDIO, - AppOpsManager.OP_PHONE_CALL_MICROPHONE) + AppOpsManager.OP_PHONE_CALL_MICROPHONE, + AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO) val OPS_LOCATION = intArrayOf( AppOpsManager.OP_COARSE_LOCATION, AppOpsManager.OP_FINE_LOCATION) @@ -315,6 +316,7 @@ class PrivacyItemController @Inject constructor( AppOpsManager.OP_COARSE_LOCATION, AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION AppOpsManager.OP_PHONE_CALL_MICROPHONE, + AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE else -> return null } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 8cef0b0130f6..b9da144713cd 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -43,6 +43,7 @@ import static android.app.AppOpsManager.OP_FLAG_SELF; import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED; import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.OP_PLAY_AUDIO; +import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD; import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_FAILED; @@ -3849,7 +3850,7 @@ public class AppOpsService extends IAppOpsService.Stub { // the data gated by OP_RECORD_AUDIO. // // TODO: Revert this change before Android 12. - if (code == OP_RECORD_AUDIO_HOTWORD) { + if (code == OP_RECORD_AUDIO_HOTWORD || code == OP_RECEIVE_AMBIENT_TRIGGER_AUDIO) { int result = checkOperation(OP_RECORD_AUDIO, uid, packageName); if (result != AppOpsManager.MODE_ALLOWED) { return new SyncNotedAppOp(result, code, attributionTag, packageName); diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java index 8de515d4d3e5..158092f33ee3 100644 --- a/services/core/java/com/android/server/appop/DiscreteRegistry.java +++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java @@ -34,6 +34,7 @@ import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXY; import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA; import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE; +import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.flagsToString; import static android.app.AppOpsManager.getUidStateName; @@ -133,7 +134,7 @@ final class DiscreteRegistry { private static final String PROPERTY_DISCRETE_OPS_LIST = "discrete_history_ops_cslist"; private static final String DEFAULT_DISCRETE_OPS = OP_FINE_LOCATION + "," + OP_COARSE_LOCATION + "," + OP_CAMERA + "," + OP_RECORD_AUDIO + "," + OP_PHONE_CALL_MICROPHONE + "," - + OP_PHONE_CALL_CAMERA; + + OP_PHONE_CALL_CAMERA + "," + OP_RECEIVE_AMBIENT_TRIGGER_AUDIO; private static final long DEFAULT_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(7).toMillis(); private static final long MAXIMUM_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(30).toMillis(); private static final long DEFAULT_DISCRETE_HISTORY_QUANTIZATION = diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerInternal.java index 446e20b70279..6c2354f1b3e6 100644 --- a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerInternal.java +++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerInternal.java @@ -17,6 +17,7 @@ package com.android.server.pm.permission; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; /** @@ -105,6 +106,20 @@ public interface LegacyPermissionManagerInternal { void scheduleReadDefaultPermissionExceptions(); /** + * Check whether a particular package should have access to the microphone data from the + * SoundTrigger. + * + * @param uid the uid of the package you are checking against + * @param packageName the name of the package you are checking against + * @param attributionTag the attributionTag to attach to the app op transaction + * @param reason the reason to attach to the app op transaction + * @return {@code PERMISSION_GRANTED} if the permission is granted, + * or {@code PERMISSION_SOFT/HARD DENIED otherwise + */ + int checkSoundTriggerRecordAudioPermissionForDataDelivery(int uid, + @NonNull String packageName, @Nullable String attributionTag, @NonNull String reason); + + /** * Provider for package names. */ interface PackagesProvider { diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java index 360a04f7e9bc..88b4a94f7027 100644 --- a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java @@ -16,12 +16,15 @@ package com.android.server.pm.permission; +import static android.Manifest.permission.RECORD_AUDIO; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.content.PermissionChecker; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; @@ -399,6 +402,21 @@ public class LegacyPermissionManagerService extends ILegacyPermissionManager.Stu public void scheduleReadDefaultPermissionExceptions() { mDefaultPermissionGrantPolicy.scheduleReadDefaultPermissionExceptions(); } + + @Override + public int checkSoundTriggerRecordAudioPermissionForDataDelivery(int uid, + @NonNull String packageName, @Nullable String attributionTag, + @NonNull String reason) { + int result = PermissionChecker.checkPermissionForPreflight(mContext, RECORD_AUDIO, -1, + uid, packageName); + if (result != PermissionChecker.PERMISSION_GRANTED) { + return result; + } + mContext.getSystemService(AppOpsManager.class).noteOpNoThrow( + AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, uid, packageName, + attributionTag, reason); + return result; + } } /** diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java index e157a277366f..a6d148c824c9 100644 --- a/services/core/java/com/android/server/policy/AppOpsPolicy.java +++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java @@ -87,8 +87,8 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat private final VoiceInteractionManagerInternal mVoiceInteractionManagerInternal; /** - * Whether this device allows only the HotwordDetectionService to use OP_RECORD_AUDIO_HOTWORD - * which doesn't incur the privacy indicator. + * Whether this device allows only the HotwordDetectionService to use + * OP_RECORD_AUDIO_HOTWORD which doesn't incur the privacy indicator. */ private final boolean mIsHotwordDetectionServiceRequired; @@ -428,8 +428,8 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat if (!mIsHotwordDetectionServiceRequired) { return code; } - // Only the HotwordDetectionService can use the HOTWORD op which doesn't incur the - // privacy indicator. Downgrade to standard RECORD_AUDIO for other processes. + // Only the HotwordDetectionService can use the RECORD_AUDIO_HOTWORD op which doesn't + // incur the privacy indicator. Downgrade to standard RECORD_AUDIO for other processes. final HotwordDetectionServiceIdentity hotwordDetectionServiceIdentity = mVoiceInteractionManagerInternal.getHotwordDetectionServiceIdentity(); if (hotwordDetectionServiceIdentity != null diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java index 3c779f387673..c354f116af5f 100644 --- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java +++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java @@ -25,6 +25,7 @@ import static android.app.AppOpsManager.MODE_IGNORED; import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA; import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE; +import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.content.Intent.EXTRA_PACKAGE_NAME; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; @@ -1067,6 +1068,10 @@ public final class SensorPrivacyService extends SystemService { mAppOpsRestrictionToken); mAppOpsManagerInternal.setGlobalRestriction(OP_PHONE_CALL_MICROPHONE, enabled, mAppOpsRestrictionToken); + // We don't show the dialog for RECEIVE_SOUNDTRIGGER_AUDIO, but still want to + // restrict it when the microphone is disabled + mAppOpsManagerInternal.setGlobalRestriction(OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, + enabled, mAppOpsRestrictionToken); break; case CAMERA: mAppOpsManagerInternal.setGlobalRestriction(OP_CAMERA, enabled, 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 f3d151fe5cc4..13fe14caa1f9 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java @@ -41,6 +41,9 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceSpecificException; +import com.android.server.LocalServices; +import com.android.server.pm.permission.LegacyPermissionManagerInternal; + import java.io.PrintWriter; import java.util.Objects; @@ -120,8 +123,7 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware * 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); + enforceSoundTriggerRecordAudioPermissionForDataDelivery(identity, reason); enforcePermissionForDataDelivery(mContext, identity, CAPTURE_AUDIO_HOTWORD, reason); } @@ -147,6 +149,19 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware } } + private static void enforceSoundTriggerRecordAudioPermissionForDataDelivery( + @NonNull Identity identity, @NonNull String reason) { + LegacyPermissionManagerInternal lpmi = + LocalServices.getService(LegacyPermissionManagerInternal.class); + final int status = lpmi.checkSoundTriggerRecordAudioPermissionForDataDelivery(identity.uid, + identity.packageName, identity.attributionTag, reason); + if (status != PermissionChecker.PERMISSION_GRANTED) { + throw new SecurityException( + String.format("Failed to obtain permission RECORD_AUDIO for identity %s", + ObjectPrinter.print(identity, 16))); + } + } + /** * Throws a {@link SecurityException} if originator permanently doesn't have the given * permission. |