Merge "SysUI: add a flag to control location indicators behavior."
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index d23ea3c..a2af4d6 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -132,6 +132,11 @@
      */
     public static final String PROPERTY_MIC_CAMERA_ENABLED = "camera_mic_icons_enabled";
 
+    /**
+     * Whether to show app ops chip for location.
+     */
+    public static final String PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled";
+
     // Flags related to Assistant
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index f56e6cd..dc5ba69 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -69,8 +69,10 @@
         private const val ALL_INDICATORS =
                 SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
         private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
+        private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
         private const val DEFAULT_ALL_INDICATORS = false
         private const val DEFAULT_MIC_CAMERA = true
+        private const val DEFAULT_LOCATION = false
     }
 
     @VisibleForTesting
@@ -88,6 +90,11 @@
         return true
     }
 
+    private fun isLocationEnabled(): Boolean {
+        return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                LOCATION, DEFAULT_LOCATION)
+    }
+
     private var currentUserIds = emptyList<Int>()
     private var listening = false
     private val callbacks = mutableListOf<WeakReference<Callback>>()
@@ -107,13 +114,15 @@
         private set
     var micCameraAvailable = isMicCameraEnabled()
         private set
+    var locationAvailable = isLocationEnabled()
 
     private val devicePropertiesChangedListener =
             object : DeviceConfig.OnPropertiesChangedListener {
         override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
             if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) &&
                     (properties.keyset.contains(ALL_INDICATORS) ||
-                    properties.keyset.contains(MIC_CAMERA))) {
+                            properties.keyset.contains(MIC_CAMERA) ||
+                            properties.keyset.contains(LOCATION))) {
 
                 // Running on the ui executor so can iterate on callbacks
                 if (properties.keyset.contains(ALL_INDICATORS)) {
@@ -126,6 +135,10 @@
 //                    micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA)
 //                    callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
 //                }
+                if (properties.keyset.contains(LOCATION)) {
+                    locationAvailable = properties.getBoolean(LOCATION, DEFAULT_LOCATION)
+                    callbacks.forEach { it.get()?.onFlagLocationChanged(locationAvailable) }
+                }
                 internalUiExecutor.updateListeningState()
             }
         }
@@ -139,7 +152,8 @@
             active: Boolean
         ) {
             // Check if we care about this code right now
-            if (!allIndicatorsAvailable && code in OPS_LOCATION) {
+            if (!allIndicatorsAvailable &&
+                    (code in OPS_LOCATION && !locationAvailable)) {
                 return
             }
             val userId = UserHandle.getUserId(uid)
@@ -195,7 +209,8 @@
      * main thread.
      */
     private fun setListeningState() {
-        val listen = !callbacks.isEmpty() and (allIndicatorsAvailable || micCameraAvailable)
+        val listen = !callbacks.isEmpty() and
+                (allIndicatorsAvailable || micCameraAvailable || locationAvailable)
         if (listening == listen) return
         listening = listen
         if (listening) {
@@ -258,7 +273,9 @@
             AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
             else -> return null
         }
-        if (type == PrivacyType.TYPE_LOCATION && !allIndicatorsAvailable) return null
+        if (type == PrivacyType.TYPE_LOCATION && (!allIndicatorsAvailable && !locationAvailable)) {
+            return null
+        }
         val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
         return PrivacyItem(type, app)
     }
@@ -271,6 +288,9 @@
 
         @JvmDefault
         fun onFlagMicCameraChanged(flag: Boolean) {}
+
+        @JvmDefault
+        fun onFlagLocationChanged(flag: Boolean) {}
     }
 
     private class NotifyChangesToCallback(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index 676a300..398edd7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -93,6 +93,7 @@
     private AlarmClockInfo mNextAlarm;
     private boolean mAllIndicatorsEnabled;
     private boolean mMicCameraIndicatorsEnabled;
+    private boolean mLocationIndicatorsEnabled;
     private boolean mPrivacyChipLogged;
     private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
 
@@ -156,6 +157,14 @@
             }
         }
 
+        @Override
+        public void onFlagLocationChanged(boolean flag) {
+            if (mLocationIndicatorsEnabled != flag) {
+                mLocationIndicatorsEnabled = flag;
+                update();
+            }
+        }
+
         private void update() {
             StatusIconContainer iconContainer = mView.requireViewById(R.id.statusIcons);
             iconContainer.setIgnoredSlots(getIgnoredIconSlots());
@@ -252,6 +261,7 @@
 
         mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
         mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
+        mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
 
         setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
 
@@ -292,6 +302,7 @@
             // Get the most up to date info
             mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
             mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
+            mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
             mPrivacyItemController.addCallback(mPICCallback);
         } else {
             mZenModeController.removeCallback(mZenModeControllerCallback);
@@ -323,7 +334,7 @@
                     com.android.internal.R.string.status_bar_camera));
             ignored.add(mView.getResources().getString(
                     com.android.internal.R.string.status_bar_microphone));
-            if (mAllIndicatorsEnabled) {
+            if (mAllIndicatorsEnabled || mLocationIndicatorsEnabled) {
                 ignored.add(mView.getResources().getString(
                         com.android.internal.R.string.status_bar_location));
             }
@@ -333,7 +344,7 @@
     }
 
     private boolean getChipEnabled() {
-        return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled;
+        return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled || mAllIndicatorsEnabled;
     }
 
     private boolean isZenOverridingRinger() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index e42c3dc..6ed092f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -671,14 +671,18 @@
 
         mIconController.setIconVisibility(mSlotCamera, showCamera);
         mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
-        if (mPrivacyItemController.getAllIndicatorsAvailable()) {
+        if (mPrivacyItemController.getAllIndicatorsAvailable()
+                || mPrivacyItemController.getLocationAvailable()) {
             mIconController.setIconVisibility(mSlotLocation, showLocation);
         }
     }
 
     @Override
     public void onLocationActiveChanged(boolean active) {
-        if (!mPrivacyItemController.getAllIndicatorsAvailable()) updateLocationFromController();
+        if (!mPrivacyItemController.getAllIndicatorsAvailable()
+                && !mPrivacyItemController.getLocationAvailable()) {
+            updateLocationFromController();
+        }
     }
 
     // Updates the status view based on the current state of location requests.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
index cb17829..cd94f84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
@@ -54,6 +54,7 @@
         private const val ALL_INDICATORS =
                 SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
         private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
+        private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED
     }
 
     @Mock
@@ -116,6 +117,15 @@
     }
 
     @Test
+    fun testLocationChanged() {
+        changeLocation(true)
+        executor.runAllReady()
+
+        verify(callback).onFlagLocationChanged(true)
+        assertTrue(privacyItemController.locationAvailable)
+    }
+
+    @Test
     fun testAllChanged() {
         changeAll(true)
         executor.runAllReady()
@@ -158,6 +168,14 @@
     }
 
     @Test
+    fun testLocation_listening() {
+        changeLocation(true)
+        executor.runAllReady()
+
+        verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
+    }
+
+    @Test
     @Ignore // TODO(b/168209929)
     fun testAllFalse_notListening() {
         changeAll(true)
@@ -205,6 +223,7 @@
     }
 
     private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
+    private fun changeLocation(value: Boolean?) = changeProperty(LOCATION, value)
     private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value)
 
     private fun changeProperty(name: String, value: Boolean?) {