Add RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO AppOp

New AppOp to be used for near-field file-based audio transfer
  (ie. TV Remote Service) to allow audio recording
  regardless of sensor privacy state, because it is considered
 an intentional user interaction: hold-to-talk

 And new Global Settings and SensorPrivacyManager APIs for the
 opt-out toggle - as agreed with PWG.

Bug: 235618473
Test: manual

Change-Id: I95cdf8fa362b937609bdf834a82b05ec8375ac26
This commit is contained in:
Valentin Iftime
2022-07-12 15:06:08 +02:00
committed by Iavor-Valentin Iftime
parent 75a803b955
commit 8f7ee077c8
8 changed files with 76 additions and 5 deletions

View File

@@ -584,6 +584,7 @@ package android.app {
field public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video";
field public static final String OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO = "android:receive_ambient_trigger_audio";
field public static final String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast";
field public static final String OPSTR_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO = "android:receive_explicit_user_interaction_audio";
field public static final String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages";
field public static final String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
field public static final String OPSTR_RUN_ANY_IN_BACKGROUND = "android:run_any_in_background";

View File

@@ -25,6 +25,7 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -1342,9 +1343,19 @@ public class AppOpsManager {
public static final int OP_RECEIVE_AMBIENT_TRIGGER_AUDIO =
AppProtoEnums.APP_OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
/**
* Receive audio from near-field mic (ie. TV remote)
* Allows audio recording regardless of sensor privacy state,
* as it is an intentional user interaction: hold-to-talk
*
* @hide
*/
public static final int OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO =
AppProtoEnums.APP_OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO;
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static final int _NUM_OP = 121;
public static final int _NUM_OP = 122;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1816,6 +1827,18 @@ public class AppOpsManager {
public static final String OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO =
"android:receive_ambient_trigger_audio";
/**
* Record audio from near-field microphone (ie. TV remote)
* Allows audio recording regardless of sensor privacy state,
* as it is an intentional user interaction: hold-to-talk
*
* @hide
*/
@SystemApi
@SuppressLint("IntentName")
public static final String OPSTR_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO =
"android:receive_explicit_user_interaction_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} */
@@ -2285,7 +2308,11 @@ public class AppOpsManager {
.setDisableReset(true).setRestrictRead(true).build(),
new AppOpInfo.Builder(OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO,
"RECEIVE_SOUNDTRIGGER_AUDIO").setDefaultMode(AppOpsManager.MODE_ALLOWED)
.setForceCollectNotes(true).build()
.setForceCollectNotes(true).build(),
new AppOpInfo.Builder(OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO,
OPSTR_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO,
"RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO").setDefaultMode(
AppOpsManager.MODE_ALLOWED).build()
};
/**

View File

@@ -16035,6 +16035,18 @@ public final class Settings {
public static final String KEY_CHORD_POWER_VOLUME_UP =
"key_chord_power_volume_up";
/**
* Record audio from near-field microphone (ie. TV remote)
* Allows audio recording regardless of sensor privacy state,
* as it is an intentional user interaction: hold-to-talk
*
* Type: int (0 to disable, 1 to enable)
*
* @hide
*/
public static final String RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO_ENABLED =
"receive_explicit_user_interaction_audio_enabled";
/**
* Keyguard should be on the left hand side of the screen, for wide screen layouts.
*

View File

@@ -84,6 +84,7 @@ public class GlobalSettings {
Settings.Global.USER_PREFERRED_RESOLUTION_HEIGHT,
Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH,
Settings.Global.POWER_BUTTON_LONG_PRESS,
Settings.Global.RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO_ENABLED,
Settings.Global.Wearable.SMART_REPLIES_ENABLED,
Settings.Global.Wearable.CLOCKWORK_AUTO_TIME,
Settings.Global.Wearable.CLOCKWORK_AUTO_TIME_ZONE,

View File

@@ -329,6 +329,8 @@ public class GlobalSettingsValidators {
VALIDATORS.put(Global.USER_PREFERRED_REFRESH_RATE, NON_NEGATIVE_FLOAT_VALIDATOR);
VALIDATORS.put(Global.USER_PREFERRED_RESOLUTION_HEIGHT, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.USER_PREFERRED_RESOLUTION_WIDTH, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO_ENABLED,
new DiscreteValueValidator(new String[]{"0", "1"}));
VALIDATORS.put(Global.Wearable.WET_MODE_ON, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.COOLDOWN_MODE_ON, BOOLEAN_VALIDATOR);
VALIDATORS.put(

View File

@@ -103,6 +103,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
AppOpsManager.OP_RECORD_AUDIO,
AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO,
AppOpsManager.OP_PHONE_CALL_MICROPHONE,
AppOpsManager.OP_COARSE_LOCATION,
AppOpsManager.OP_FINE_LOCATION

View File

@@ -56,7 +56,8 @@ class AppOpsPrivacyItemMonitor @Inject constructor(
val OPS_MIC_CAMERA = intArrayOf(AppOpsManager.OP_CAMERA,
AppOpsManager.OP_PHONE_CALL_CAMERA, AppOpsManager.OP_RECORD_AUDIO,
AppOpsManager.OP_PHONE_CALL_MICROPHONE,
AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO)
AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO)
val OPS_LOCATION = intArrayOf(
AppOpsManager.OP_COARSE_LOCATION,
AppOpsManager.OP_FINE_LOCATION)
@@ -210,6 +211,7 @@ class AppOpsPrivacyItemMonitor @Inject constructor(
AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION
AppOpsManager.OP_PHONE_CALL_MICROPHONE,
AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO,
AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
else -> return null
}

View File

@@ -26,6 +26,7 @@ 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_RECEIVE_EXPLICIT_USER_INTERACTION_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;
@@ -81,6 +82,7 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.graphics.drawable.Icon;
import android.hardware.ISensorPrivacyListener;
import android.hardware.ISensorPrivacyManager;
@@ -199,6 +201,7 @@ public final class SensorPrivacyService extends SystemService {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
mCallStateHelper = new CallStateHelper();
mSensorPrivacyServiceImpl.registerSettingsObserver();
} else if (phase == PHASE_ACTIVITY_MANAGER_READY) {
mCameraPrivacyLightController = new CameraPrivacyLightController(mContext);
}
@@ -271,7 +274,7 @@ public final class SensorPrivacyService extends SystemService {
mSensorPrivacyStateController = SensorPrivacyStateController.getInstance();
int[] micAndCameraOps = new int[]{OP_RECORD_AUDIO, OP_PHONE_CALL_MICROPHONE,
OP_CAMERA, OP_PHONE_CALL_CAMERA};
OP_CAMERA, OP_PHONE_CALL_CAMERA, OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO};
mAppOpsManager.startWatchingNoted(micAndCameraOps, this);
mAppOpsManager.startWatchingStarted(micAndCameraOps, this);
@@ -340,7 +343,8 @@ public final class SensorPrivacyService extends SystemService {
int sensor;
if (result == MODE_IGNORED) {
if (code == OP_RECORD_AUDIO || code == OP_PHONE_CALL_MICROPHONE) {
if (code == OP_RECORD_AUDIO || code == OP_PHONE_CALL_MICROPHONE
|| code == OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO) {
sensor = MICROPHONE;
} else if (code == OP_CAMERA || code == OP_PHONE_CALL_CAMERA) {
sensor = CAMERA;
@@ -1072,6 +1076,14 @@ public final class SensorPrivacyService extends SystemService {
// restrict it when the microphone is disabled
mAppOpsManagerInternal.setGlobalRestriction(OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
enabled, mAppOpsRestrictionToken);
// Set restriction for OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO
boolean allowed = (Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO_ENABLED, 1)
== 1);
mAppOpsManagerInternal.setGlobalRestriction(
OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO, enabled && !allowed,
mAppOpsRestrictionToken);
break;
case CAMERA:
mAppOpsManagerInternal.setGlobalRestriction(OP_CAMERA, enabled,
@@ -1112,6 +1124,19 @@ public final class SensorPrivacyService extends SystemService {
}
}
private void registerSettingsObserver() {
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(
Settings.Global.RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO_ENABLED),
false, new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
setGlobalRestriction(MICROPHONE,
isCombinedToggleSensorPrivacyEnabled(MICROPHONE));
}
});
}
/**
* A owner of a suppressor token died. Clean up.
*