1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.settings.privacy; 18 19 import static com.android.car.settings.enterprise.EnterpriseUtils.getAvailabilityStatusRestricted; 20 import static com.android.car.settings.enterprise.EnterpriseUtils.hasUserRestrictionByDpm; 21 import static com.android.car.settings.enterprise.EnterpriseUtils.onClickWhileDisabled; 22 23 import android.car.drivingstate.CarUxRestrictions; 24 import android.content.Context; 25 import android.hardware.SensorPrivacyManager; 26 import android.os.UserManager; 27 28 import com.android.car.settings.common.ColoredSwitchPreference; 29 import com.android.car.settings.common.FragmentController; 30 import com.android.car.settings.common.PreferenceController; 31 import com.android.internal.annotations.VisibleForTesting; 32 33 /** Business logic for controlling the mute mic toggle. */ 34 public class MicTogglePreferenceController extends PreferenceController<ColoredSwitchPreference> { 35 36 private final SensorPrivacyManager mSensorPrivacyManager; 37 private final SensorPrivacyManager.OnSensorPrivacyChangedListener mListener = 38 (sensor, enabled) -> refreshUi(); 39 MicTogglePreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)40 public MicTogglePreferenceController(Context context, String preferenceKey, 41 FragmentController fragmentController, 42 CarUxRestrictions uxRestrictions) { 43 this(context, preferenceKey, fragmentController, uxRestrictions, 44 SensorPrivacyManager.getInstance(context)); 45 } 46 47 @VisibleForTesting MicTogglePreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions, SensorPrivacyManager sensorPrivacyManager)48 MicTogglePreferenceController(Context context, String preferenceKey, 49 FragmentController fragmentController, CarUxRestrictions uxRestrictions, 50 SensorPrivacyManager sensorPrivacyManager) { 51 super(context, preferenceKey, fragmentController, uxRestrictions); 52 mSensorPrivacyManager = sensorPrivacyManager; 53 } 54 55 @Override getPreferenceType()56 protected Class<ColoredSwitchPreference> getPreferenceType() { 57 return ColoredSwitchPreference.class; 58 } 59 60 @Override onStartInternal()61 protected void onStartInternal() { 62 mSensorPrivacyManager.addSensorPrivacyListener( 63 SensorPrivacyManager.Sensors.MICROPHONE, mListener); 64 } 65 66 @Override onStopInternal()67 protected void onStopInternal() { 68 mSensorPrivacyManager.removeSensorPrivacyListener(SensorPrivacyManager.Sensors.MICROPHONE, 69 mListener); 70 } 71 72 @Override handlePreferenceChanged(ColoredSwitchPreference preference, Object newValue)73 protected boolean handlePreferenceChanged(ColoredSwitchPreference preference, 74 Object newValue) { 75 boolean isChecked = (Boolean) newValue; 76 // Settings UX currently shows "checked means mic is enabled", but the underlying API is 77 // inversely written around "is mic muted?" So we must be careful when doing 78 // comparisons. 79 boolean isMicMuted = mSensorPrivacyManager.isSensorPrivacyEnabled( 80 SensorPrivacyManager.Sensors.MICROPHONE); 81 if (isChecked == isMicMuted) { 82 // UX and underlying API state for mic do not match, so update sensor privacy 83 mSensorPrivacyManager.setSensorPrivacyForProfileGroup( 84 SensorPrivacyManager.Sources.SETTINGS, 85 SensorPrivacyManager.Sensors.MICROPHONE, 86 !isChecked); 87 } 88 return true; 89 } 90 91 @Override getDefaultAvailabilityStatus()92 protected int getDefaultAvailabilityStatus() { 93 if (!mSensorPrivacyManager.supportsSensorToggle(SensorPrivacyManager.Sensors.MICROPHONE)) { 94 return UNSUPPORTED_ON_DEVICE; 95 } else { 96 return getAvailabilityStatusRestricted(getContext(), 97 UserManager.DISALLOW_MICROPHONE_TOGGLE); 98 } 99 } 100 101 @Override updateState(ColoredSwitchPreference preference)102 protected void updateState(ColoredSwitchPreference preference) { 103 preference.setChecked(!mSensorPrivacyManager.isSensorPrivacyEnabled( 104 SensorPrivacyManager.Sensors.MICROPHONE)); 105 if (hasUserRestrictionByDpm(getContext(), UserManager.DISALLOW_MICROPHONE_TOGGLE)) { 106 setClickableWhileDisabled(preference, /* clickable= */ true, p -> 107 onClickWhileDisabled(getContext(), getFragmentController(), 108 UserManager.DISALLOW_MICROPHONE_TOGGLE)); 109 } 110 } 111 } 112