Loading core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +6 −1 Original line number Diff line number Diff line Loading @@ -123,10 +123,15 @@ public final class SystemUiDeviceConfigFlags { // Flag related to Privacy Indicators /** * Whether the Permissions Hub is showing. * Whether to show the complete ongoing app ops chip. */ public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_2_enabled"; /** * Whether to show app ops chip for just microphone + camera. */ public static final String PROPERTY_MIC_CAMERA_ENABLED = "camera_mic_icons_enabled"; // Flags related to Assistant /** Loading packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +5 −3 Original line number Diff line number Diff line Loading @@ -281,9 +281,11 @@ public class AppOpsControllerImpl implements AppOpsController, * @return {@code true} iff the app-op for should be shown to the user */ private boolean isUserVisible(int appOpCode, int uid, String packageName) { // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission // which may be user senstive, so for now always show it to the user. if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) { // currently OP_SYSTEM_ALERT_WINDOW and OP_MONITOR_HIGH_POWER_LOCATION // does not correspond to a platform permission // which may be user sensitive, so for now always show it to the user. if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW || appOpCode == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) { return true; } Loading packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +45 −19 Original line number Diff line number Diff line Loading @@ -45,7 +45,6 @@ import javax.inject.Singleton @Singleton class PrivacyItemController @Inject constructor( context: Context, private val appOpsController: AppOpsController, @Main uiExecutor: DelayableExecutor, @Background private val bgExecutor: Executor, Loading @@ -57,16 +56,21 @@ class PrivacyItemController @Inject constructor( @VisibleForTesting internal companion object { val OPS = intArrayOf(AppOpsManager.OP_CAMERA, AppOpsManager.OP_RECORD_AUDIO, val OPS_MIC_CAMERA = intArrayOf(AppOpsManager.OP_CAMERA, AppOpsManager.OP_RECORD_AUDIO) val OPS_LOCATION = intArrayOf( AppOpsManager.OP_COARSE_LOCATION, AppOpsManager.OP_FINE_LOCATION) val OPS = OPS_MIC_CAMERA + OPS_LOCATION val intentFilter = IntentFilter().apply { addAction(Intent.ACTION_USER_SWITCHED) addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE) } const val TAG = "PrivacyItemController" private const val ALL_INDICATORS = SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED } @VisibleForTesting Loading @@ -74,9 +78,14 @@ class PrivacyItemController @Inject constructor( @Synchronized get() = field.toList() // Returns a shallow copy of the list @Synchronized set private fun isPermissionsHubEnabled(): Boolean { private fun isAllIndicatorsEnabled(): Boolean { return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false) ALL_INDICATORS, false) } private fun isMicCameraEnabled(): Boolean { return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, MIC_CAMERA, false) } private var currentUserIds = emptyList<Int>() Loading @@ -94,23 +103,28 @@ class PrivacyItemController @Inject constructor( uiExecutor.execute(notifyChanges) } var indicatorsAvailable = isPermissionsHubEnabled() var allIndicatorsAvailable = isAllIndicatorsEnabled() private set @VisibleForTesting internal val devicePropertiesChangedListener = var micCameraAvailable = isMicCameraEnabled() private set private val devicePropertiesChangedListener = object : DeviceConfig.OnPropertiesChangedListener { override fun onPropertiesChanged(properties: DeviceConfig.Properties) { if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) && properties.getKeyset().contains( SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED)) { val flag = properties.getBoolean( SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false) if (indicatorsAvailable != flag) { // This is happening already in the UI executor, so we can iterate in the indicatorsAvailable = flag callbacks.forEach { it.get()?.onFlagChanged(flag) } (properties.keyset.contains(ALL_INDICATORS) || properties.keyset.contains(MIC_CAMERA))) { // Running on the ui executor so can iterate on callbacks if (properties.keyset.contains(ALL_INDICATORS)) { allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS, false) callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) } } if (properties.keyset.contains(MIC_CAMERA)) { micCameraAvailable = properties.getBoolean(MIC_CAMERA, false) callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) } } internalUiExecutor.updateListeningState() } } Loading @@ -123,6 +137,10 @@ class PrivacyItemController @Inject constructor( packageName: String, active: Boolean ) { // Check if we care about this code right now if (!allIndicatorsAvailable && code in OPS_LOCATION) { return } val userId = UserHandle.getUserId(uid) if (userId in currentUserIds) { update(false) Loading Loading @@ -166,13 +184,16 @@ class PrivacyItemController @Inject constructor( } /** * Updates listening status based on whether there are callbacks and the indicators are enabled * Updates listening status based on whether there are callbacks and the indicators are enabled. * * Always listen to all OPS so we don't have to figure out what we should be listening to. We * still have to filter anyway. Updates are filtered in the callback. * * This is only called from private (add/remove)Callback and from the config listener, all in * main thread. */ private fun setListeningState() { val listen = !callbacks.isEmpty() and indicatorsAvailable val listen = !callbacks.isEmpty() and (allIndicatorsAvailable || micCameraAvailable) if (listening == listen) return listening = listen if (listening) { Loading Loading @@ -233,14 +254,19 @@ class PrivacyItemController @Inject constructor( AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE else -> return null } if (type == PrivacyType.TYPE_LOCATION && !allIndicatorsAvailable) return null val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid) return PrivacyItem(type, app) } interface Callback { fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) @JvmDefault fun onFlagAllChanged(flag: Boolean) {} @JvmDefault fun onFlagChanged(flag: Boolean) {} fun onFlagMicCameraChanged(flag: Boolean) {} } internal inner class Receiver : BroadcastReceiver() { Loading packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +37 −15 Original line number Diff line number Diff line Loading @@ -151,7 +151,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements private Space mSpace; private BatteryMeterView mBatteryRemainingIcon; private RingerModeTracker mRingerModeTracker; private boolean mPermissionsHubEnabled; private boolean mAllIndicatorsEnabled; private boolean mMicCameraIndicatorsEnabled; private PrivacyItemController mPrivacyItemController; private final UiEventLogger mUiEventLogger; Loading @@ -178,13 +179,26 @@ public class QuickStatusBarHeader extends RelativeLayout implements } @Override public void onFlagChanged(boolean flag) { if (mPermissionsHubEnabled != flag) { public void onFlagAllChanged(boolean flag) { if (mAllIndicatorsEnabled != flag) { mAllIndicatorsEnabled = flag; update(); } } @Override public void onFlagMicCameraChanged(boolean flag) { if (mMicCameraIndicatorsEnabled != flag) { mMicCameraIndicatorsEnabled = flag; update(); } } private void update() { StatusIconContainer iconContainer = requireViewById(R.id.statusIcons); iconContainer.setIgnoredSlots(getIgnoredIconSlots()); setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty()); } } }; @Inject Loading Loading @@ -267,7 +281,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements mRingerModeTextView.setSelected(true); mNextAlarmTextView.setSelected(true); mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable(); mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); } public QuickQSPanel getHeaderQsPanel() { Loading @@ -276,14 +291,16 @@ public class QuickStatusBarHeader extends RelativeLayout implements private List<String> getIgnoredIconSlots() { ArrayList<String> ignored = new ArrayList<>(); if (getChipEnabled()) { ignored.add(mContext.getResources().getString( com.android.internal.R.string.status_bar_camera)); ignored.add(mContext.getResources().getString( com.android.internal.R.string.status_bar_microphone)); if (mPermissionsHubEnabled) { if (mAllIndicatorsEnabled) { ignored.add(mContext.getResources().getString( com.android.internal.R.string.status_bar_location)); } } return ignored; } Loading @@ -300,7 +317,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements } private void setChipVisibility(boolean chipVisible) { if (chipVisible && mPermissionsHubEnabled) { if (chipVisible && getChipEnabled()) { mPrivacyChip.setVisibility(View.VISIBLE); // Makes sure that the chip is logged as viewed at most once each time QS is opened // mListening makes sure that the callback didn't return after the user closed QS Loading Loading @@ -607,7 +624,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements mAlarmController.addCallback(this); mLifecycle.setCurrentState(Lifecycle.State.RESUMED); // Get the most up to date info mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable(); mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); mPrivacyItemController.addCallback(mPICCallback); } else { mZenController.removeCallback(this); Loading Loading @@ -747,4 +765,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements updateHeaderTextContainerAlphaAnimator(); } } private boolean getChipEnabled() { return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled; } } packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +5 −3 Original line number Diff line number Diff line Loading @@ -662,16 +662,18 @@ public class PhoneStatusBarPolicy mIconController.setIconVisibility(mSlotCamera, showCamera); mIconController.setIconVisibility(mSlotMicrophone, showMicrophone); if (mPrivacyItemController.getAllIndicatorsAvailable()) { mIconController.setIconVisibility(mSlotLocation, showLocation); } } @Override public void onLocationActiveChanged(boolean active) { if (!mPrivacyItemController.getIndicatorsAvailable()) updateLocation(); if (!mPrivacyItemController.getAllIndicatorsAvailable()) updateLocationFromController(); } // Updates the status view based on the current state of location requests. private void updateLocation() { private void updateLocationFromController() { if (mLocationController.isLocationActive()) { mIconController.setIconVisibility(mSlotLocation, true); } else { Loading Loading
core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +6 −1 Original line number Diff line number Diff line Loading @@ -123,10 +123,15 @@ public final class SystemUiDeviceConfigFlags { // Flag related to Privacy Indicators /** * Whether the Permissions Hub is showing. * Whether to show the complete ongoing app ops chip. */ public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_2_enabled"; /** * Whether to show app ops chip for just microphone + camera. */ public static final String PROPERTY_MIC_CAMERA_ENABLED = "camera_mic_icons_enabled"; // Flags related to Assistant /** Loading
packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +5 −3 Original line number Diff line number Diff line Loading @@ -281,9 +281,11 @@ public class AppOpsControllerImpl implements AppOpsController, * @return {@code true} iff the app-op for should be shown to the user */ private boolean isUserVisible(int appOpCode, int uid, String packageName) { // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission // which may be user senstive, so for now always show it to the user. if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) { // currently OP_SYSTEM_ALERT_WINDOW and OP_MONITOR_HIGH_POWER_LOCATION // does not correspond to a platform permission // which may be user sensitive, so for now always show it to the user. if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW || appOpCode == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) { return true; } Loading
packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +45 −19 Original line number Diff line number Diff line Loading @@ -45,7 +45,6 @@ import javax.inject.Singleton @Singleton class PrivacyItemController @Inject constructor( context: Context, private val appOpsController: AppOpsController, @Main uiExecutor: DelayableExecutor, @Background private val bgExecutor: Executor, Loading @@ -57,16 +56,21 @@ class PrivacyItemController @Inject constructor( @VisibleForTesting internal companion object { val OPS = intArrayOf(AppOpsManager.OP_CAMERA, AppOpsManager.OP_RECORD_AUDIO, val OPS_MIC_CAMERA = intArrayOf(AppOpsManager.OP_CAMERA, AppOpsManager.OP_RECORD_AUDIO) val OPS_LOCATION = intArrayOf( AppOpsManager.OP_COARSE_LOCATION, AppOpsManager.OP_FINE_LOCATION) val OPS = OPS_MIC_CAMERA + OPS_LOCATION val intentFilter = IntentFilter().apply { addAction(Intent.ACTION_USER_SWITCHED) addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE) } const val TAG = "PrivacyItemController" private const val ALL_INDICATORS = SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED } @VisibleForTesting Loading @@ -74,9 +78,14 @@ class PrivacyItemController @Inject constructor( @Synchronized get() = field.toList() // Returns a shallow copy of the list @Synchronized set private fun isPermissionsHubEnabled(): Boolean { private fun isAllIndicatorsEnabled(): Boolean { return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false) ALL_INDICATORS, false) } private fun isMicCameraEnabled(): Boolean { return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, MIC_CAMERA, false) } private var currentUserIds = emptyList<Int>() Loading @@ -94,23 +103,28 @@ class PrivacyItemController @Inject constructor( uiExecutor.execute(notifyChanges) } var indicatorsAvailable = isPermissionsHubEnabled() var allIndicatorsAvailable = isAllIndicatorsEnabled() private set @VisibleForTesting internal val devicePropertiesChangedListener = var micCameraAvailable = isMicCameraEnabled() private set private val devicePropertiesChangedListener = object : DeviceConfig.OnPropertiesChangedListener { override fun onPropertiesChanged(properties: DeviceConfig.Properties) { if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) && properties.getKeyset().contains( SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED)) { val flag = properties.getBoolean( SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false) if (indicatorsAvailable != flag) { // This is happening already in the UI executor, so we can iterate in the indicatorsAvailable = flag callbacks.forEach { it.get()?.onFlagChanged(flag) } (properties.keyset.contains(ALL_INDICATORS) || properties.keyset.contains(MIC_CAMERA))) { // Running on the ui executor so can iterate on callbacks if (properties.keyset.contains(ALL_INDICATORS)) { allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS, false) callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) } } if (properties.keyset.contains(MIC_CAMERA)) { micCameraAvailable = properties.getBoolean(MIC_CAMERA, false) callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) } } internalUiExecutor.updateListeningState() } } Loading @@ -123,6 +137,10 @@ class PrivacyItemController @Inject constructor( packageName: String, active: Boolean ) { // Check if we care about this code right now if (!allIndicatorsAvailable && code in OPS_LOCATION) { return } val userId = UserHandle.getUserId(uid) if (userId in currentUserIds) { update(false) Loading Loading @@ -166,13 +184,16 @@ class PrivacyItemController @Inject constructor( } /** * Updates listening status based on whether there are callbacks and the indicators are enabled * Updates listening status based on whether there are callbacks and the indicators are enabled. * * Always listen to all OPS so we don't have to figure out what we should be listening to. We * still have to filter anyway. Updates are filtered in the callback. * * This is only called from private (add/remove)Callback and from the config listener, all in * main thread. */ private fun setListeningState() { val listen = !callbacks.isEmpty() and indicatorsAvailable val listen = !callbacks.isEmpty() and (allIndicatorsAvailable || micCameraAvailable) if (listening == listen) return listening = listen if (listening) { Loading Loading @@ -233,14 +254,19 @@ class PrivacyItemController @Inject constructor( AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE else -> return null } if (type == PrivacyType.TYPE_LOCATION && !allIndicatorsAvailable) return null val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid) return PrivacyItem(type, app) } interface Callback { fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) @JvmDefault fun onFlagAllChanged(flag: Boolean) {} @JvmDefault fun onFlagChanged(flag: Boolean) {} fun onFlagMicCameraChanged(flag: Boolean) {} } internal inner class Receiver : BroadcastReceiver() { Loading
packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +37 −15 Original line number Diff line number Diff line Loading @@ -151,7 +151,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements private Space mSpace; private BatteryMeterView mBatteryRemainingIcon; private RingerModeTracker mRingerModeTracker; private boolean mPermissionsHubEnabled; private boolean mAllIndicatorsEnabled; private boolean mMicCameraIndicatorsEnabled; private PrivacyItemController mPrivacyItemController; private final UiEventLogger mUiEventLogger; Loading @@ -178,13 +179,26 @@ public class QuickStatusBarHeader extends RelativeLayout implements } @Override public void onFlagChanged(boolean flag) { if (mPermissionsHubEnabled != flag) { public void onFlagAllChanged(boolean flag) { if (mAllIndicatorsEnabled != flag) { mAllIndicatorsEnabled = flag; update(); } } @Override public void onFlagMicCameraChanged(boolean flag) { if (mMicCameraIndicatorsEnabled != flag) { mMicCameraIndicatorsEnabled = flag; update(); } } private void update() { StatusIconContainer iconContainer = requireViewById(R.id.statusIcons); iconContainer.setIgnoredSlots(getIgnoredIconSlots()); setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty()); } } }; @Inject Loading Loading @@ -267,7 +281,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements mRingerModeTextView.setSelected(true); mNextAlarmTextView.setSelected(true); mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable(); mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); } public QuickQSPanel getHeaderQsPanel() { Loading @@ -276,14 +291,16 @@ public class QuickStatusBarHeader extends RelativeLayout implements private List<String> getIgnoredIconSlots() { ArrayList<String> ignored = new ArrayList<>(); if (getChipEnabled()) { ignored.add(mContext.getResources().getString( com.android.internal.R.string.status_bar_camera)); ignored.add(mContext.getResources().getString( com.android.internal.R.string.status_bar_microphone)); if (mPermissionsHubEnabled) { if (mAllIndicatorsEnabled) { ignored.add(mContext.getResources().getString( com.android.internal.R.string.status_bar_location)); } } return ignored; } Loading @@ -300,7 +317,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements } private void setChipVisibility(boolean chipVisible) { if (chipVisible && mPermissionsHubEnabled) { if (chipVisible && getChipEnabled()) { mPrivacyChip.setVisibility(View.VISIBLE); // Makes sure that the chip is logged as viewed at most once each time QS is opened // mListening makes sure that the callback didn't return after the user closed QS Loading Loading @@ -607,7 +624,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements mAlarmController.addCallback(this); mLifecycle.setCurrentState(Lifecycle.State.RESUMED); // Get the most up to date info mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable(); mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); mPrivacyItemController.addCallback(mPICCallback); } else { mZenController.removeCallback(this); Loading Loading @@ -747,4 +765,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements updateHeaderTextContainerAlphaAnimator(); } } private boolean getChipEnabled() { return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled; } }
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +5 −3 Original line number Diff line number Diff line Loading @@ -662,16 +662,18 @@ public class PhoneStatusBarPolicy mIconController.setIconVisibility(mSlotCamera, showCamera); mIconController.setIconVisibility(mSlotMicrophone, showMicrophone); if (mPrivacyItemController.getAllIndicatorsAvailable()) { mIconController.setIconVisibility(mSlotLocation, showLocation); } } @Override public void onLocationActiveChanged(boolean active) { if (!mPrivacyItemController.getIndicatorsAvailable()) updateLocation(); if (!mPrivacyItemController.getAllIndicatorsAvailable()) updateLocationFromController(); } // Updates the status view based on the current state of location requests. private void updateLocation() { private void updateLocationFromController() { if (mLocationController.isLocationActive()) { mIconController.setIconVisibility(mSlotLocation, true); } else { Loading